DF-0145 / df0145_pop.c
/* * DF-0145 - vq_done stub leaks all quota RB-trees on unmount. * * sys/kern/vfs_quota.c:142-146: * void vq_done(struct mount *mp) { [TODO: remove the rb trees here] } * * On mount, VFS_ACINIT (vfs_vfsops.c:112) -> vfs_stdac_init -> vq_init sets up * the per-mount RB trees and enables accounting. Accounting population * (vfs_stdaccount / cmd_set_limit_uid -> unode_insert/gnode_insert) kmalloc's * ac_unode / ac_gnode (M_MOUNT). On unmount, VFS_ACDONE (vfs_vfsops.c:131) -> * vfs_stdac_done -> vq_done is the empty stub, so every node is leaked. * * This populator drives cmd_set_limit_uid -> unode_insert for N distinct uids * on a mounted, quota-enabled path, growing the ac_uroot RB tree. The * accompanying run0145.sh mounts tmpfs there, runs this populator, records * M_MOUNT, unmounts (triggering the leak), and records M_MOUNT again. * * Build: cc -o df0145_pop df0145_pop.c -lprop */ #include <sys/types.h> #include <sys/vfs_quota.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <libprop/proplib.h> static int set_limit_uid(const char *path, uid_t uid, uint64_t limit) { prop_dictionary_t dict, args, res = NULL; struct plistref pref; int error; dict = prop_dictionary_create(); prop_dictionary_set_cstring(dict, "command", "set limit uid"); args = prop_dictionary_create(); prop_dictionary_set_uint32(args, "uid", uid); prop_dictionary_set_uint64(args, "limit", limit); prop_dictionary_set(dict, "arguments", args); error = prop_dictionary_send_syscall(dict, &pref); if (error == 0) error = vquotactl(path, &pref); if (error == 0) prop_dictionary_recv_syscall(&pref, &res); prop_object_release(dict); if (res) prop_object_release(res); return error; } int main(int argc, char **argv) { const char *path = argc > 1 ? argv[1] : "/mnt/df0145"; int n = argc > 2 ? atoi(argv[2]) : 200; int i, ok = 0; /* distinct uid chunks (uid >> 5) force distinct unode_insert() calls */ for (i = 0; i < n; i++) { uid_t uid = (uid_t)i * 32; if (set_limit_uid(path, uid, 1ULL<<20) == 0) ok++; } printf("populated %d/%d uid chunks on %s (each -> unode_insert kmalloc M_MOUNT)\n", ok, n, path); return 0; } |