⬢ DragonFlyBSD Kernel Audit
DF-2573 / ngdpoc_shim_prepend.h
← back to finding ↓ download raw
/*
 * NGDPOC shim prologue -- DF-2573 / DF-2574 verification scaffolding.
 *
 * sys/netgraph/ng_device.c is bit-rotted dead code: it is absent from
 * sys/conf/files and from the module SUBDIR list, and it programs against
 * the 2003-era DragonFly device API (`struct cdevsw`, make_dev(cdevsw*),
 * d_*_t(cdev_t,...) signatures, cdevsw_add) that no longer exists in the
 * tree (the live API is `struct dev_ops` + args-struct trampolines).
 *
 * To verify the code UNDER TEST byte-verbatim, this prologue is PREPENDED
 * to an unmodified copy of ng_device.c (see gen_ngdpoc.sh) and provides
 * ONLY the missing infrastructure:
 *   - the old `struct cdevsw` definition matching the file's positional
 *     initializer,
 *   - an interposed make_dev() that (a) forwards to the REAL
 *     make_dev()/devfs with a modern dev_ops trampoline so a REAL
 *     /dev/ngd%d appears, and (b) can be armed to fail exactly the Nth
 *     call (DF-2573 fault injection, scoped to this file only),
 *   - no-op macros for symbols that no longer exist (cdevsw_add,
 *     cdevsw_remove, nommap, nostrategy, nodump, nopsize),
 *   - typedef renames so the file's old-style d_*_t declarations compile
 *     against modern headers WITHOUT touching the file body.
 *
 * Nothing in ng_device.c lines 1..633 (the verbatim region) is modified.
 * The fault-injection knobs themselves live in kern/df_ngdpoc.c.
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>			/* real make_dev(struct dev_ops*,...), destroy_dev(cdev_t) */
#include <sys/device.h>			/* modern dev_ops + d_*_t + dev_*_args */
#include <sys/malloc.h>
#include <sys/types.h>
#include <machine/stdint.h>

/* fault-injection knobs -- defined in kern/df_ngdpoc.c */
extern int ngdpoc_fail_makedev_nth;	/* DF-2573: fail next N shim make_dev() calls */
extern int ngdpoc_kmalloc_fail_armed;	/* DF-2573: one-shot kmalloc failure (see kern_slaballoc.c patch) */
extern unsigned long ngdpoc_kmalloc_fail_size;

struct thread;

/* ---- old-style (2003-era) cdevsw API, exactly what ng_device.c expects ---- */

typedef int ngdpoc_d_open_t(cdev_t dev, int oflags, int devtype, struct thread *td);
typedef int ngdpoc_d_close_t(cdev_t dev, int fflag, int devtype, struct thread *td);
typedef int ngdpoc_d_read_t(cdev_t dev, struct uio *uio, int ioflag);
typedef int ngdpoc_d_write_t(cdev_t dev, struct uio *uio, int ioflag);
typedef int ngdpoc_d_ioctl_t(cdev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td);
typedef int ngdpoc_d_poll_t(cdev_t dev, int events, struct thread *td);
typedef int ngdpoc_d_mmap_t(cdev_t dev, vm_offset_t offset, int nprot);
typedef void ngdpoc_d_strategy_t(cdev_t dev, struct bio *bio);
typedef int ngdpoc_d_dump_t(cdev_t dev, void *virtual, vm_offset_t physical, off_t offset, size_t length);
typedef int ngdpoc_d_psize_t(cdev_t dev);

struct cdevsw {
	const char	*name;
	int		maj;
	int		flags;
	void		*port;
	void		*clone;
	ngdpoc_d_open_t		*open;
	ngdpoc_d_close_t	*close;
	ngdpoc_d_read_t		*read;
	ngdpoc_d_write_t	*write;
	ngdpoc_d_ioctl_t	*ioctl;
	ngdpoc_d_poll_t		*poll;
	ngdpoc_d_mmap_t		*mmap;
	ngdpoc_d_strategy_t	*strategy;
	ngdpoc_d_dump_t		*dump;
	ngdpoc_d_psize_t	*psize;
};

/* stubs replacing the kern_device.c statics referenced by the verbatim
 * initializer (they are only stored, the trampoline dev_ops below never
 * routes mmap/strategy/dump/psize to them) */
static int	ngdpoc_nommap(cdev_t dev, vm_offset_t off, int nprot) { return (-1); }
static void	ngdpoc_nostrategy(cdev_t dev, struct bio *bio) { }
static int	ngdpoc_nodump(cdev_t dev, void *v, vm_offset_t p, off_t o, size_t l) { return (ENXIO); }
static int	ngdpoc_nopsize(cdev_t dev) { return (0); }

/*
 * Trampoline dev_ops: translates the modern args-struct device API onto the
 * verbatim file's old-signature driver methods (ngdopen/ngdclose/ngdread/
 * ngdwrite/ngdioctl).  All units share the same static cdevsw.
 */
static struct cdevsw *ngdpoc_csw;

static int
ngdpoc_t_open(struct dev_open_args *ap)
{
	return (ngdpoc_csw->open(ap->a_head.a_dev, ap->a_oflags, ap->a_devtype, NULL));
}

static int
ngdpoc_t_close(struct dev_close_args *ap)
{
	return (ngdpoc_csw->close(ap->a_head.a_dev, ap->a_fflag, ap->a_devtype, NULL));
}

static int
ngdpoc_t_read(struct dev_read_args *ap)
{
	return (ngdpoc_csw->read(ap->a_head.a_dev, ap->a_uio, ap->a_ioflag));
}

static int
ngdpoc_t_write(struct dev_write_args *ap)
{
	return (ngdpoc_csw->write(ap->a_head.a_dev, ap->a_uio, ap->a_ioflag));
}

static int
ngdpoc_t_ioctl(struct dev_ioctl_args *ap)
{
	return (ngdpoc_csw->ioctl(ap->a_head.a_dev, ap->a_cmd, ap->a_data, ap->a_fflag, NULL));
}

static struct dev_ops ngdpoc_real_ops = {
	{ "ngd", 197, 0, NULL, 0, 0 },		/* head; maj 197 unused on this guest */
	.d_open		= ngdpoc_t_open,
	.d_close	= ngdpoc_t_close,
	.d_read		= ngdpoc_t_read,
	.d_write	= ngdpoc_t_write,
	.d_ioctl	= ngdpoc_t_ioctl,
};

/*
 * Interposed make_dev(): this is the ONLY symbol the verbatim file calls
 * that must be renamed, because the real make_dev() takes a dev_ops*.
 *
 * DF-2573 fault injection: when armed (kern.ngdpoc.fail_makedev_nth > 0),
 * the Nth call FROM THIS FILE returns NULL -- precisely the
 * ng_device_newhook() error path at ng_device.c:288.  Zero global impact:
 * every other make_dev() caller in the kernel is untouched.
 */
cdev_t	ngdpoc_make_dev(struct cdevsw *csw, int minor, uid_t uid, gid_t gid,
	    int perms, const char *fmt, ...);

/* accessors implemented in the epilogue */
size_t	ngdpoc_conn_size(void);
int	ngdpoc_list_count(void);
void	ngdpoc_list_dump(char *buf, size_t len);

cdev_t
ngdpoc_make_dev(struct cdevsw *csw, int minor, uid_t uid, gid_t gid,
		int perms, const char *fmt, ...)
{
	char name[64];
	__va_list ap;

	if (ngdpoc_fail_makedev_nth > 0 && --ngdpoc_fail_makedev_nth == 0) {
		kprintf("NGDPOC: injected make_dev() failure\n");
		return (NULL);
	}
	ngdpoc_csw = csw;
	compile_dev_ops(&ngdpoc_real_ops);
	__va_start(ap, fmt);
	kvsnprintf(name, sizeof(name), fmt, ap);
	__va_end(ap);
	return (make_dev(&ngdpoc_real_ops, minor, uid, gid, perms, "%s", name));
}

/*
 * From here on, the verbatim ng_device.c text is compiled against the shim:
 * make_dev -> ngdpoc_make_dev, the old d_*_t typedefs, and no-op macros for
 * symbols that no longer exist anywhere in the tree.
 */
#define make_dev		ngdpoc_make_dev
#define d_open_t		ngdpoc_d_open_t
#define d_close_t		ngdpoc_d_close_t
#define d_read_t		ngdpoc_d_read_t
#define d_write_t		ngdpoc_d_write_t
#define d_ioctl_t		ngdpoc_d_ioctl_t
#define d_poll_t		ngdpoc_d_poll_t
#define nommap			ngdpoc_nommap
#define nostrategy		ngdpoc_nostrategy
#define nodump			ngdpoc_nodump
#define nopsize			ngdpoc_nopsize
#define cdevsw_add(a, b, c)			do { } while (0)
#define cdevsw_remove(a, b, c)			do { } while (0)

/* ng_device.c:103 redefines MIN(a,b) with a different macro body than
 * sys/param.h -- drop the param.h one so the verbatim definition stands */
#undef MIN