Hi Matthew, On Wed, Oct 11, 2023 at 11:11:24AM -0400, Matthew House wrote: > On Wed, Oct 11, 2023 at 10:47 AM Alejandro Colomar <alx@xxxxxxxxxx> wrote: > > On Wed, Oct 11, 2023 at 09:44:29AM -0400, Matthew House wrote: > > > To expand on this, there are basically two separate byte limits in > > > fs/exec.c, one for each individual argv/envp string, and another for all > > > strings and all pointers to them as a whole. To put the whole thing in > > > pseudocode, the checks work effectively like this, assuming I haven't made > > > any errors: > > > > > > int argc, envc; > > > unsigned long bytes, limit; > > > > > > /* assume that argv has already been adjusted to add an empty argv[0] */ > > > argc = 0, envc = 0, bytes = 0; > > > for (char **a = argv; *a != NULL; a++, argc++) { > > > if (strlen(*a) >= MAX_ARG_STRLEN) > > > > Are you sure this is >= and not > ? > > Yes. In general, the kernel's string limits tend to include the trailing > null byte. There are two places where this limit is enforced, one for the > pathname (or full pathname for execveat) and the other for the argv/envp > strings. The pathname is handled by copy_string_kernel(): > > int len = strnlen(arg, MAX_ARG_STRLEN) + 1 /* terminating NUL */; > > if (len == 0) > return -EFAULT; > if (!valid_arg_len(bprm, len)) > return -E2BIG; > > where valid_arg_len(bprm, len) is just (len <= MAX_ARG_STRLEN). Here, > strnlen() has the same behavior as the ordinary libc strnlen(3), > effectively returning min(strlen(arg), MAX_ARG_STRLEN). Thus, the check > succeeds iff strlen(arg) + 1 <= MAX_ARG_STRLEN, or equivalently, iff > strlen(arg) < MAX_ARG_STRLEN. > > Next, each of the environment and argument strings is handled by > copy_strings(): > > len = strnlen_user(str, MAX_ARG_STRLEN); > if (!len) > goto out; > > ret = -E2BIG; > if (!valid_arg_len(bprm, len)) > goto out; > > The strnlen_user() function, per its documentation, is explicitly inclusive > of the trailing null byte: > > * Returns the size of the string INCLUDING the terminating NUL. > * If the string is too long, returns a number larger than @count. User > * has to check the return value against "> count". > * On exception (or invalid count), returns 0. > > Thus, the check succeeds iff the size including the null byte is > <= MAX_ARG_STRLEN, i.e., iff strlen(arg) + 1 <= MAX_ARG_STRLEN, or > strlen(arg) < MAX_ARG_STRLEN. Thanks! It's a bit confusing to see the terms 'len' and '_STRLEN' meaning length+1, but it makes sense now. Cheers, Alex > > Matthew House -- <https://www.alejandro-colomar.es/>
Attachment:
signature.asc
Description: PGP signature