Index | Thread | Search

From:
Walter Alejandro Iglesias <wai@roquesor.com>
Subject:
snprintf() misuse
To:
tech@openbsd.org
Date:
Thu, 15 Aug 2024 20:01:12 +0200

Download raw body.

Thread
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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <stdint.h>
#include <unistd.h>
#include <ctype.h>
#include <locale.h>

#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