The ELF e_phnum field is only 16 bits wide. To support more than 65535
segments (program headers), Sun's "Linker and Libraries Guide" table 7-7
prescribes a special first section header where sh_info represents the real
number of program headers.
Details
#include <sys/param.h> #include <sys/mman.h> #include <err.h> #include <stdint.h> #include <stdlib.h> int main(int argc __unused, char **argv __unused) { void *v; unsigned i; for (i = 0; i < UINT16_MAX + 1000; i++) { v = mmap(NULL, PAGE_SIZE, (((i % 2) == 0) ? PROT_READ : 0) | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (v == MAP_FAILED) err(1, "mmap"); } abort(); }
(Testing is still a work in progress, building a kernel on this laptop is pretty slow.)
Please CC anyone you think may be qualified or interested to review this.
Diff Detail
- Repository
- rS FreeBSD src repository - subversion
- Lint
Lint Not Applicable - Unit
Tests Not Applicable
Event Timeline
sys/kern/imgact_elf.c | ||
---|---|---|
1695–1697 ↗ | (On Diff #18568) | Ignore this, I intend to remove it before committing. |
Does that test program actually work? I'd have thought that the consecutive mmaps would just extend a single vm_map entry, resulting in a single segment. You could defeat that by alternating the protections on consecutive pages with something like "i % 2 ? PROT_READ : PROT_WRITE".
@markj: Yeah, that (alternating -w-/rw-) and adding a few extra (UINT16_MAX + 1000) does it.
$ readelf -l /var/crash/wklptp-phnum_overflow-71842.core Elf file type is CORE (Core file) Entry point 0x0 There are 633 program headers, starting at offset 64 ... $ procstat -v /var/crash/wklptp-phnum_overflow-71842.core | wc -l 66545
After patch:
$ procstat -v /var/crash/wklptp-phnum_overflow-1027.core | wc -l 66545 $ readelf -l /var/crash/wklptp-phnum_overflow-1027.core | wc -l 132346 $ readelf -l /var/crash/wklptp-phnum_overflow-1027.core | head Elf file type is CORE (Core file) Entry point 0x0 There are 66169 program headers, starting at offset 64 … $ readelf -S /var/crash/wklptp-phnum_overflow-1027.core There are 1 section headers, starting at offset 0x388ab8: Section Header: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] <no-name> NULL 0000000000000000 00000000 0000000000000001 0000000000000000 0 66169 0 …
66169 % 65536 = 633
0x388ab8 (SH offset) = 64 (PH offset) + 56 (PH entity size) * 66169 (num PH)
Looks ok to me. It's not really clear to me how standard this is, but it's better than nothing, and I've found a few other references to this trick elsewhere.
sys/kern/imgact_elf.c | ||
---|---|---|
1667 ↗ | (On Diff #18593) | Missing a period. |
Fine with me, although perhaps expand the comment slightly (e.g. something like "a special section is used to hold large segment counts")?
The test code could go in tests/sys/kern?
Well, Linux and Solaris use the same technique, at least. And I didn't have to modify readelf(1) to be aware of it; it already knew about the special section.
Good point.
The test code could go in tests/sys/kern?
I'll have to spend a little time figuring out how to use that.
I'll have to spend a little time figuring out how to use that.
I think it should be reasonably straightforward to just copy one of the examples already in there, e.g. unix_passfd_test.c or ptrace_test.c.