⬢ DragonFlyBSD Kernel Audit
DF-5711 / harness_netmap_clamp.c
← back to finding ↓ download raw
/* DF-5711: netmap_vale.c:331-332 clamp off-by-one.
 *
 * 	if (namelen >= IFNAMSIZ)
 * 		namelen = IFNAMSIZ;          <-- should be IFNAMSIZ - 1
 * 	...
 * 	strncpy(b->bdg_basename, name, namelen);
 *
 * bdg_basename is char[IFNAMSIZ] (netmap_vale.c:198). A bridge name of
 * >=16 non-NUL chars makes strncpy write exactly 16 non-NUL bytes and the
 * array is left unterminated. Demonstrated here with the kernel strncpy
 * from /usr/src/sys/libkern/strncpy.c; the struct analogue embeds
 * bdg_basename before a canary so an OOB strlen is observable.
 */
#include <stdio.h>
#include <string.h>

char *kstrncpy(char *dst, const char *src, size_t n); /* kernel source */

#define IFNAMSIZ 16

struct bridge_shim {
	char bdg_basename[IFNAMSIZ];
	char canary[32];
};

int
main(void)
{
	struct bridge_shim b;
	char name[64] = "vale123456789abcd";	/* 16 chars, no NUL until 16 */
	int namelen = (int)strlen(name);

	memset(&b, 0x41, sizeof(b));
	b.canary[sizeof(b.canary) - 1] = 0;	/* NUL only after the canary */

	if (namelen >= IFNAMSIZ)
		namelen = IFNAMSIZ;		/* netmap_vale.c:331-332 verbatim */
	kstrncpy(b.bdg_basename, name, (size_t)namelen);

	{
		int nul = -1, i;
		for (i = 0; i < IFNAMSIZ; i++)
			if (b.bdg_basename[i] == 0) { nul = i; break; }
		printf("bdg_basename has NUL within [0,%d)? -> %s (index %d)\n",
		    IFNAMSIZ, nul >= 0 ? "YES" : "NO", nul);
		printf("strlen(bdg_basename) (what any %%s consumer reads): %zu\n",
		    strlen(b.bdg_basename));
		printf("printf(\"%%s\") reads: '%s'\n", b.bdg_basename);
	}
	printf("\nin-tree consumers of bdg_basename: ND() at netmap_vale.c:353,482\n"
	       "and D() at :566 -- ND/D are no-ops in release (netmap_kern.h:80-81)\n"
	       "-> latent non-termination, no live consumer: hardening.\n");
	return 0;
}