⬢ DragonFlyBSD Kernel Audit
DF-3130 / remount_leak.c
← back to finding ↓ download raw
/*
 * DF-3130: drive msdosfs_mount(MNT_UPDATE) with args.fspec != NULL and the
 * KICONV flag set, in a loop.  update_mp() (msdosfs_vfsops.c:115-133)
 * re-opens all four iconv handles (pm_w2u/pm_u2w/pm_u2d/pm_d2u) on every
 * call and overwrites the pm_* pointers without closing the previous ones
 * -> 4 leaked converter handles per remount until unmount.
 *
 * Mirrors mount_msdos(8): same header (<vfs/msdosfs/msdosfsmount.h>), same
 * kiconv_add_xlat16_cspairs() pre-registration of the conversion pairs.
 *
 * usage: remount_leak <device> <mountpoint> <iterations> [cs_local cs_dos]
 */
#include <stdio.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/iconv.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <vfs/msdosfs/msdosfsmount.h>

int main(int argc, char **argv)
{
	struct msdosfs_args args;
	const char *cs_local = "zh_CN.GB18030";
	const char *cs_dos = "GB18030";
	int i, n, ok = 0, fail = 0;

	if (argc < 4) {
		fprintf(stderr, "usage: remount_leak <device> <mountpoint> <iterations> [cs_local cs_dos]\n");
		return 2;
	}
	if (argc >= 6) {
		cs_local = argv[4];
		cs_dos = argv[5];
	}
	n = atoi(argv[3]);

	/*
	 * No userland kiconv registration needed: pm_flags retains
	 * MSDOSFSMNT_KICONV from the initial mount_msdos -L call (sticky,
	 * msdosfs_vfsops.c:113), and the conversion tables registered by the
	 * CLI are still in the kernel, so update_mp() re-opens the four
	 * handles on every MNT_UPDATE even without KICONV in *these* args.
	 */

	for (i = 0; i < n; i++) {
		memset(&args, 0, sizeof(args));
		args.fspec = argv[1];
		args.uid = 0;
		args.gid = 0;
		args.mask = 0755;
		args.dirmask = 0755;
		args.flags = MSDOSFSMNT_LONGNAME;
		snprintf(args.cs_local, sizeof(args.cs_local), "%s", cs_local);
		snprintf(args.cs_dos, sizeof(args.cs_dos), "%s", cs_dos);
		if (mount("msdos", argv[2], MNT_UPDATE, &args) == 0)
			ok++;
		else {
			fail++;
			if (fail <= 3)
				fprintf(stderr, "mount UPDATE failed: %s\n",
					strerror(errno));
		}
	}
	printf("remounts: ok=%d failed=%d\n", ok, fail);
	return 0;
}