posix_spawn Argument Validation Vulnerability
posix_spawn is a complex syscall which takes a lot of arguments from userspace. The third argument is a pointer to a further arguments descriptor in userspace with the following structure (on 32-bit): struct user32__posix_spawn_args_desc { uint32_t attr_size; /* size of attributes block */ uint32_t attrp; /* pointer to block */ uint32_t file_actions_size; /* size of file actions block */ uint32_t file_actions; /* pointer to block */ uint32_t port_actions_size; /* size of port actions block */ uint32_t port_actions; /* pointer to block */ uint32_t mac_extensions_size; uint32_t mac_extensions; uint32_t coal_info_size; uint32_t coal_info; uint32_t persona_info_size; uint32_t persona_info; } port_actions then points to another structure in userspace of this type: struct _posix_spawn_port_actions { int pspa_alloc; int pspa_count; _ps_port_action_t pspa_actions[]; } and finally _ps_port_action_t looks like this: struct _ps_port_action { pspa_t port_type; exception_mask_t mask; mach_port_name_t new_port; exception_behavior_t behavior; thread_state_flavor_t flavor; int which; } Note that pspa_actions is a zero-sized array. pspa_count is supposed to be the number of entries in this array. The following constraints are checked in posix_spawn in kern_exec.c: if (px_args.port_actions_size != 0) { /* Limit port_actions to one page of data */ if (px_args.port_actions_size < PS_PORT_ACTIONS_SIZE(1) || px_args.port_actions_size > PAGE_SIZE) { error = EINVAL; goto bad; PS_PORT_ACTIONS_SIZE is defined like this: #define PS_PORT_ACTIONS_SIZE(x) __offsetof(struct _posix_spawn_port_actions, pspa_actions[(x)]) if port_actions_size passes this then we reach the following code: MALLOC(px_spap, _posix_spawn_port_actions_t, px_args.port_actions_size, M_TEMP, M_WAITOK); if (px_spap == NULL) { error = ENOMEM; goto bad; }