From: Walter Alejandro Iglesias Subject: snprintf() misuse To: tech@openbsd.org Date: Thu, 15 Aug 2024 20:01:12 +0200 After reading an article by Todd C. Miller, I realized that I was misusing snprintf(). Following Todd's advice I changed it to strndup() and the testing program below gained performance significantly. Because of this and some annoying bug that took me a while to fix, I rewrote my encode_word() function. Updated in my patches: https://en.roquesor.com/Downloads/mail_patches.tar.gz /* * Little testing program. */ #include #include #include #include #include #include #include #include #include #define b64enc __b64_ntop static int newline_at_end = 0; void encode_word(char **p); static void filecopy(FILE *, FILE *); int b64enc(unsigned char const *src, size_t srclength, char *target, size_t targsize); void wrap_header(char **p, size_t name_len); void usage(void); int main(int argc, char *argv[]) { FILE *fp; int option; while ((option = getopt(argc, argv, "h")) != -1) switch (option) { case 'h': usage(); break; default: usage(); } argc -= optind; argv += optind; if (argc > 0) while (argc-- > 0) if ((fp = fopen(*argv++, "r")) == NULL) warn("%s", *(argv - 1)); else { filecopy(fp, stdout); fclose(fp); } else filecopy(stdin, stdout); return errno; } void filecopy(FILE * ifp, FILE * ofp) { char *p = NULL; size_t size = 0; size_t i = 0; int c; while ((c = getc(ifp)) != EOF) { if (i == size) { p = realloc(p, size + 100); if (p == NULL) err(1, NULL); size += 100; } /* Strip control characters */ if (!iscntrl(c) || c == '\t' || c == '\n') p[i++] = c; } if (i == size) { p = realloc(p, size + 1); if (p == NULL) err(1, NULL); } p[i] = '\0'; encode_word(&p); wrap_header(&p, 0); printf("%s", p); free(p); } void encode_word(char **p) { char *s = NULL; char *r = NULL; char *word = NULL; char *eword = NULL; size_t i = 0; size_t n = 0; size_t size = 0; size_t ck = 0; size_t sp = 0; size_t wlen = 0; size_t elen = 0; char ftag[] = "=?UTF-8?B?"; char otag[] = " =?UTF-8?B?"; char ctag[] = "?="; size_t flen = strlen(ftag); size_t olen = strlen(otag); size_t clen = strlen(ctag); int encoded = 0; setlocale(LC_CTYPE, "en_US.UTF-8"); size = strlen(*p); s = malloc(size); if (s == NULL) err(1, NULL); r = *p; while (r[n] != '\0') { if (encoded == 0 && isspace(r[n])) s[i++] = r[n++]; sp = strspn(&r[n], "\t\n "); wlen = strcspn(&r[n + sp], "\t\n "); wlen += sp; word = strndup(&r[n], wlen); if (word == NULL) err(1, NULL); /* Check if the word contains UTF-8 characters */ if (wlen > mbstowcs(NULL, word, 0)) { elen = (wlen + 2) / 3 * 4; eword = malloc(elen + 1); if (eword == NULL) err(1, NULL); b64enc(word, wlen, eword, elen); eword[elen] = '\0'; if (encoded > 0) { s = realloc(s, size + olen + clen + (elen - wlen) + 1); if (s == NULL) err(1, NULL); size += olen + clen + (elen - wlen) + 1; ck = snprintf(&s[i], elen + olen + clen + 1, "%s%s%s", otag, eword, ctag); if (ck >= elen + olen + clen + 1) errx(1, "snprint: toolong"); i += elen + olen + clen; } else { s = realloc(s, size + flen + clen + (elen - wlen) + 1); if (s == NULL) err(1, NULL); size += flen + clen + (elen - wlen) + 1; ck = snprintf(&s[i], elen + flen + clen + 1, "%s%s%s", ftag, eword, ctag); if (ck >= elen + flen + clen + 1) errx(1, "snprint: toolong"); i += elen + flen + clen; } free(eword); n += wlen; encoded++; } else { ck = snprintf(&s[i], wlen + 1, "%s", word); if (ck >= wlen + 1) errx(1, "snprint: toolong"); i += wlen; n += wlen; encoded = 0; } free(word); } if (i == size) { s = realloc(s, size + 1); if (s == NULL) err(1, NULL); } s[i] = '\0'; /* Remove from mail patch */ free(r); *p = s; } void wrap_header(char **p, size_t name_len) { char *s = NULL; char *r = NULL; size_t i = 0; size_t n = 0; size_t size = 0; int col = name_len; /* let room for header name */ int wrap = 72; size = strlen(*p); s = malloc(size); if (s == NULL) err(1, NULL); r = *p; while (r[n] != '\0') { /* Replace newlines by spaces */ if (r[n] == '\n' && r[n + 1] != '\0') r[n] = ' '; if (n != 0 && isspace(r[n]) && col + strcspn(&r[n + 1], "\t\n ") >= wrap) { s = realloc(s, size + 1); if (s == NULL) err(1, NULL); size++; if (r[n + 1] != '\0') s[i++] = '\n'; while (isspace(r[n + 1])) n++; col = 0; if (r[n] == '\t') col += 7; } s[i++] = r[n++]; if (r[n] == '\t') col += 7; col++; } if (i == size) { s = realloc(s, size + 1); if (s == NULL) err(1, NULL); } s[i] = '\0'; /* Remove this free from mail patch */ free(r); *p = s; } void usage(void) { extern char *__progname; fprintf(stderr, "Usage: %s [-bhlnp] [-w width] [file ...]\n" " -h print this help\n", __progname); exit(1); } -- Walter