Hi,

I have reviewed Waldek's message from a few days ago regarding the issue with 
Linux 5.8 kernel. Waldek's patch to the linux_setper function  in c_core.c 
makes complete sense and should do the job.

Before the patch strace shows this:

steve@dev001:/tmp$ grep -C 2 personality p_trace_before_patch.txt 
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, 
rlim_max=RLIM64_INFINITY}) = 0
uname({sysname="Linux", nodename="dev001.42d.io", ...}) = 0
personality(0xffffffff)                 = 0 (PER_LINUX)
personality(PER_LINUX|ADDR_NO_RANDOMIZE) = 0 (PER_LINUX)
personality(0xffffffff)                 = 0x40000 (PER_LINUX|ADDR_NO_RANDOMIZE)
readlink("/proc/self/exe", "/tmp/newpop11", 4096) = 13
execve("/tmp/newpop11", ["./newpop11"], 0x7ffdf421ca58 /* 70 vars */) = 0
--
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, 
rlim_max=RLIM64_INFINITY}) = 0
uname({sysname="Linux", nodename="dev001.42d.io", ...}) = 0
personality(0xffffffff)                 = 0x40000 (PER_LINUX|ADDR_NO_RANDOMIZE)
brk(NULL)                               = 0x62c000
rt_sigaction(SIGQUIT, {sa_handler=0x46ca70, sa_mask=[], 
sa_flags=SA_RESTORER|SA_INTERRUPT|SA_SIGINFO, sa_restorer=0x7ffff77e0950}, 
{sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0

After the patch we see an endless loop like this:

prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, 
rlim_max=RLIM64_INFINITY}) = 0
uname({sysname="Linux", nodename="dev001.42d.io", ...}) = 0
personality(0xffffffff)                 = 0 (PER_LINUX)
personality(PER_LINUX|ADDR_NO_RANDOMIZE|READ_IMPLIES_EXEC) = 0 (PER_LINUX)
personality(0xffffffff)                 = 0x440000 
(PER_LINUX|ADDR_NO_RANDOMIZE|READ_IMPLIES_EXEC)
readlink("/proc/self/exe", "/tmp/newpop11", 4096) = 13
execve("/tmp/newpop11", ["./newpop11"], 0x7ffdf09bb0b8 /* 70 vars */) = 0
--
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, 
rlim_max=RLIM64_INFINITY}) = 0
uname({sysname="Linux", nodename="dev001.42d.io", ...}) = 0
personality(0xffffffff)                 = 0x40000 (PER_LINUX|ADDR_NO_RANDOMIZE)
personality(PER_LINUX|ADDR_NO_RANDOMIZE|READ_IMPLIES_EXEC) = 0x40000 
(PER_LINUX|ADDR_NO_RANDOMIZE)
personality(0xffffffff)                 = 0x440000 
(PER_LINUX|ADDR_NO_RANDOMIZE|READ_IMPLIES_EXEC)
readlink("/proc/self/exe", "/tmp/newpop11", 4096) = 13
execve("/tmp/newpop11", ["./newpop11"], 0x7fffffffd8e8 /* 70 vars */) = 0
--
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, 
rlim_max=RLIM64_INFINITY}) = 0
uname({sysname="Linux", nodename="dev001.42d.io", ...}) = 0
personality(0xffffffff)                 = 0x40000 (PER_LINUX|ADDR_NO_RANDOMIZE)
personality(PER_LINUX|ADDR_NO_RANDOMIZE|READ_IMPLIES_EXEC) = 0x40000 
(PER_LINUX|ADDR_NO_RANDOMIZE)
personality(0xffffffff)                 = 0x440000 
(PER_LINUX|ADDR_NO_RANDOMIZE|READ_IMPLIES_EXEC)
readlink("/proc/self/exe", "/tmp/newpop11", 4096) = 13
execve("/tmp/newpop11", ["./newpop11"], 0x7fffffffd8e8 /* 70 vars */) = 0
--

In other words, it appears that the reset personality flag READ_IMPLIES_EXEC is 
not persisting through the execve. Now this might be because it's not relevant 
in 64-bit applications, which is how I read a note in 
https://outflux.net/blog/archives/2021/02/08/security-things-in-linux-v5-8/ 
<https://outflux.net/blog/archives/2021/02/08/security-things-in-linux-v5-8/>. 
This says:

> READ_IMPLIES_EXEC is no more for native 64-bit
> The READ_IMPLIES_EXEC flag was a work-around for dealing with the addition of 
> non-executable (NX) memory when x86_64 was introduced. It was designed as a 
> way to mark a memory region as “well, since we don’t know if this memory 
> region was expected to be executable, we must assume that if we need to read 
> it, we need to be allowed to execute it too”. It was designed mostly for 
> stack memory (where trampoline code might live), but it would carry over into 
> all mmap() allocations, which would mean sometimes exposing a large attack 
> surface to an attacker looking to find executable memory. While normally this 
> didn’t cause problems on modern systems that correctly marked their ELF 
> sections as NX, there were still some awkward corner-cases. I fixed this by 
> splitting READ_IMPLIES_EXEC from the ELF PT_GNU_STACK marking on x86 
> <https://git.kernel.org/linus/122306117afe4ba202b5e57c61dfbeffc5c41387> and 
> arm/arm64 
> <https://git.kernel.org/linus/eaf3f9e61887332d5097dbf0b327b8377546adc5>, and 
> declaring that a native 64-bit process would never gain READ_IMPLIES_EXEC on 
> x86_64 
> <https://git.kernel.org/linus/9fccc5c0c99f238aa1b0460fccbdb30a887e7036> and 
> arm64 
> <https://git.kernel.org/linus/6e0d6ac5f3d9d90271899f6d340872360fe1caee>, 
> which matches the behavior of other native 64-bit architectures that 
> correctly didn’t ever implement READ_IMPLIES_EXEC in the first place.

If the READ_IMPLIES_EXEC flag is always masked out then Waldek's patch would, 
indeed, give rise to an infinite loop. So the next question is whether or not 
the PT_GNU_STACK flag is applied. You can find this out using the execstack 
utility, as per this webpage https://linux.die.net/man/8/execstack 
<https://linux.die.net/man/8/execstack>

Unfortunately I can't install execstack on my test system and I don't have easy 
access to a 5.8 linux myself. Maybe someone else could check?

Cheers,

Steve

Reply via email to