ip_dummynet_compat doesn't validate sopt_valsize correctly.
The IP_DUMMYNET_DEL and IP_DUMMYNET_CONFIGURE commands allocate and copyin using this size, and then pass the buffer onto dn_compat_del and dn_compat_configure respectively. There are several issues with this.
Passing sizes less than the sizes of the expected structs, for example 0, would result in allocating and copying 0 bytes, and then passing this buffer onto these functions, where it is dereferenced as a structure of a different size, leading to out of bounds heap access.
As a side note, is7 is a global variable, and so this code can be raced. A process could sabotage another process' setsockopt call by making its own setsockopt request to change the is7 value whilst the other setsockopt call is in mid-execution. An easy fix for this would be to take a lock around ip_dummynet_compat calls, however it would potentially be cleaner to refactor this code to pass the is7 value as an argument to function calls.
int ip_dummynet_compat(struct sockopt *sopt) { int error=0; void *v = NULL; struct dn_id oid; /* Length of data, used to found ipfw version... */ int len = sopt->sopt_valsize; /* len can be 0 if command was dummynet_flush */ if (len == pipesize7) { D("setting compatibility with FreeBSD 7.2"); is7 = 1; } else if (len == pipesize8 || len == pipesizemax8) { D("setting compatibility with FreeBSD 8"); is7 = 0; } switch (sopt->sopt_name) { ... case IP_DUMMYNET_DEL: v = malloc(len, M_TEMP, M_WAITOK); error = sooptcopyin(sopt, v, len, len); if (error) break; error = dn_compat_del(v); free(v, M_TEMP); break; case IP_DUMMYNET_CONFIGURE: v = malloc(len, M_TEMP, M_WAITOK); error = sooptcopyin(sopt, v, len, len); if (error) break; error = dn_compat_configure(v); free(v, M_TEMP); break; ...
The fix for this issue is to return EINVAL for unexpected sizes.