Bug#1001961: No longer properly hooks the stat call

2021-12-20 Thread Christoph Biedl
Control: tags 1001961 patch

Christoph Biedl wrote...

> it seems recent changes in libc6 caused the stat() call in C applications
> to be expanded in a different way, a way fakeroot does not properly
> handle, resulting in the real user-id, not 0.

Fix appended (inline, not a patch) for better readability. Tested
successfully on the following archs: amd64, i386, armel, armhf, arm64;
with both fakeroot(-sysv) and fakeroot-tcp.

Cheers,

Christoph

Subject: Also wrap the "stat" library call
Author: Christoph Biedl 
Date: 2021-12-20
Bug-Debian: https://bugs.debian.org/1001961

Seems changes in glibc 2.33 caused the stat() function to be mapped
into a stat() library call instead of __xstat() as it used to be.

However, fakeroot does not wrap this, causing files to be reported
with the real owner, not 0 as expected.

The fix for this got more ugly than wished as the abstraction in
configure.ac would not allow wrapping both "stat" and "__xstat". So
enhance the search list capabilities with an optional symbol how the
wrapped function is named internally. Also hack the parser so
"state" gets actually probed.

Using "realstat" is not the best choice as it might be confusing,
but "statstat" seemed even worse.

--- a/configure.ac
+++ b/configure.ac
@@ -353,9 +353,13 @@
 
 :>fakerootconfig.h.tmp
 
-for SEARCH in %stat f%stat l%stat f%statat %stat64 f%stat64 l%stat64 
f%statat64 %mknod %mknodat; do
-  FUNC=`echo $SEARCH|sed -e 's/.*%//'`
+for SEARCH in %stat s%tat@realstat f%stat l%stat f%statat %stat64 f%stat64 
l%stat64 f%statat64 %mknod %mknodat; do
+  FUNC=`echo $SEARCH|sed -e 's/.*%// ; s/@.*//'`
   PRE=`echo $SEARCH|sed -e 's/%.*//'`
+  PF_PRE=`echo $SEARCH|sed -e 's/.*@//'`
+  if test "$PF_PRE" = "$SEARCH" ; then
+PF_PRE="${PRE}${FUNC}"
+  fi
   FOUND=
   for WRAPPED in __${PRE}x${FUNC} _${PRE}x${FUNC} __${PRE}${FUNC}13 
${PRE}${FUNC}; do
 AC_CHECK_FUNCS($WRAPPED,FOUND=$WRAPPED)
@@ -366,8 +370,8 @@
 dnl  for WRAPPED in _${PRE}${FUNC}; do
 dnlFOUND=$WRAPPED
 if test -n "$FOUND"; then
-  PF=[`echo ${PRE}${FUNC}| tr '[a-z]' '[A-Z]'`]
-  DEFINE_WRAP=[`echo wrap_${PRE}${FUNC}| tr '[a-z]' '[A-Z]'`]
+  PF=[`echo $PF_PRE | tr '[a-z]' '[A-Z]'`]
+  DEFINE_WRAP=[`echo wrap_${PF_PRE}| tr '[a-z]' '[A-Z]'`]
   DEFINE_NEXT=[`echo wrap_${FOUND}| tr '[a-z]' '[A-Z]'`]
   DEFINE_ARG=[`echo wrap_${FOUND}| tr '[a-z]' '[A-Z]'`]
   AC_DEFINE_UNQUOTED(WRAP_${PF}, $FOUND)
@@ -509,6 +513,12 @@
 #define TMP_STAT  __astat
 #define NEXT_STAT_NOARG  next___astat
 
+#define WRAP_REALSTAT  __astat
+#define WRAP_REALSTAT_QUOTE  __astat
+#define WRAP_REALSTAT_RAW  __astat
+#define TMP_REALSTAT  __astat
+#define NEXT_REALSTAT_NOARG  next___astat
+
 #define WRAP_LSTAT_QUOTE  __astat
 #define WRAP_LSTAT  __astat
 #define WRAP_LSTAT_RAW  __astat


signature.asc
Description: PGP signature


Bug#1001961: No longer properly hooks the stat call

2021-12-19 Thread Christoph Biedl
Package: fakeroot
Version: 1.26-1
Severity: critical
Justification: Breaks unrelated software, possibly affects built packages

Hello,

it seems recent changes in libc6 caused the stat() call in C applications
to be expanded in a different way, a way fakeroot does not properly
handle, resulting in the real user-id, not 0.


Reproducer

Use this small programm that stats the given directory and
prints its uid:

===

#include 
#include 
#include 

int main (int argc, char **argv) {
struct stat statbuf;

if (argc != 2) {
fprintf(stderr, "usage: %s \n", argv[0]);
exit(1);
}

if (stat (argv[1], &statbuf)) {
perror("Cannot stat");
exit(1);
}

printf("uid is %u\n", statbuf.st_uid);
}

===

Environments

* Debian 11 ("bullseye") or unstable with libc6 still on 2.32
* Debian unstable

Then run "fakeroot ./a.out ."

Expected output:

uid is 0

Actual output on current unstable:

uid is 1000

(or whatever your user id is)


This problem is part of the built program, not of the environment
it is executed in.


Additionally, if run without fakeroot, the program calls as follows:

strace, old version

stat(".", {st_mode=S_IFDIR|0755, st_size=19, ...}) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x504), ...}) = 0

strace, new version

newfstatat(AT_FDCWD, ".", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x228), 
...}, AT_EMPTY_PATH) = 0

ltrace, old version

__xstat(1, ".", 0x7ffeb9c0a260) = 0

ltrace, new version

stat(0x7ffc5fef250d, 0x7ffc5fef07b0, 0x7ffc5fef07b0, 0x7f0122b05738) = 0


Impact:

This broke the python-apt autopkgtest after uploading a new version of
gnugp2, and even does when using just the old version but re-built
using a current libc6.

Actual breakage is caused by the fact gpg checks for the permissions of
its home directory and emits a warning to stderr for possibly insecure
settings. The python-apt test runs under fakeroot, and while getuid()
returns 0 as expected, the stat() call now yields the actual user-id
(1000 or whatever) for the directory. And any message to stderr causes
autopkgtest to assume failure.

No further checks where done but I am concerned this might affect
packages that still use fakeroot for building. If Debian starts shipping
packages where file ownership should be root but is not, we have a
problem. Scanning my local package cache, I have no indication this
happened for far.


Additionally:

Adrian Bunk mentioned this has been fixed in Ubuntu, a quick check
confirms that for 22.04.


Christoph

-- System Information:
Debian Release: bookworm/sid
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: amd64 (x86_64)

Kernel: Linux 5.10.84 (SMP w/8 CPU threads)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages fakeroot depends on:
ii  libc62.33-1
ii  libfakeroot  1.26-1

fakeroot recommends no packages.

fakeroot suggests no packages.

-- no debconf information



signature.asc
Description: PGP signature