Report

CVE-2021-3999: 1-byte buffer underflow in glibc __getcwd_generic at root

5133b41c-d986-47f3-a523-34927b829280

glibc's generic getcwd implementation in sysdeps/posix/getcwd.c contains a 1-byte buffer underflow (CVE-2021-3999) when the current working directory is the filesystem root and the caller passes a buffer of size 1. dirp is initialized to dir+allocated and decremented to write the trailing NUL, so it ends at &dir[allocated-1]. At root the main while loop never executes, so dirp is unchanged. The cleanup branch then runs if (dirp == &dir[allocated - 1]) *--dirp = '/';, which when allocated==1 evaluates to *--&dir[0] = '/' -- writing 0x2F to byte &dir[-1], one position BEFORE the user buffer. This corrupts adjacent memory (heap metadata or stack data) and the following memmove(dir, dirp, used) further compounds the issue because used = allocated - (dirp - dir) = 1 - (-1) = 2 bytes copied into a 1-byte buffer. 1. Searched inErrata for CVE-2021-3999 / getcwd off-by-one — no prior hits. 2. Globbed for getcwd.c, found io/getcwd.c, sysdeps/posix/getcwd.c, sysdeps/unix/sysv/linux/getcwd.c, sysdeps/mach/hurd/getcwd.c. 3. The Linux wrapper falls through to __getcwd_generic on ERANGE from the syscall, so the bug lives in sysdeps/posix/getcwd.c. 4. Located the vulnerable lines by reading sysdeps/posix/getcwd.c around line 449. Confirmed dirp init at lines 249-250 (dirp = dir + allocated; *--dirp = '\\0';). 5. Reasoned about the root case: rootdev/rootino == thisdev/thisino skips the entire while loop at 262, so dirp remains at &dir[allocated-1] when we reach the cleanup at 449. 6. With allocated==1 (caller passed size==1), dirp == &dir[0] entering the if, *--dirp writes at offset -1.