diff mbox series

linux: Use __libc_procutils_read_file on __readonly_area

Message ID 20241226205027.3865649-1-adhemerval.zanella@linaro.org
State New
Headers show
Series linux: Use __libc_procutils_read_file on __readonly_area | expand

Commit Message

Adhemerval Zanella Netto Dec. 26, 2024, 8:50 p.m. UTC
The assert/test-assert-2 overrides malloc to check if assert works
when malloc is not avaiable.  However, when fortification is enabled
__aprintf checks if the format is on a read-only area with
__readonly_area, and since it uses fopen (which calls malloc) the
prints ends up failing due a fortify abort.

This patch replace the fopen usage with __libc_procutils_read_file_ext,
which uses a static buffer.  The buffer used on current
__libc_procutils_read_file (PROCUTILS_MAX_LINE_LEN/256) is not suitable
to read the /proc/self/maps because the pathname can be up to PATH_MAX.

It removes the malloc usage from fopen and fixes the assert/test-assert-2
regression when glibc is configured with fortification.

Checked on aarch64-linux-gnu.
---
 sysdeps/unix/sysv/linux/procutils.c     |  29 ++++--
 sysdeps/unix/sysv/linux/procutils.h     |   8 ++
 sysdeps/unix/sysv/linux/readonly-area.c | 125 ++++++++++++------------
 3 files changed, 96 insertions(+), 66 deletions(-)
diff mbox series

Patch

diff --git a/sysdeps/unix/sysv/linux/procutils.c b/sysdeps/unix/sysv/linux/procutils.c
index 86d3d37329..55f332ed58 100644
--- a/sysdeps/unix/sysv/linux/procutils.c
+++ b/sysdeps/unix/sysv/linux/procutils.c
@@ -70,13 +70,10 @@  next_line (char **r, int fd, char *const buffer, char **cp, char **re,
   return 1;
 }
 
-bool
-__libc_procutils_read_file (const char *filename,
-			    procutils_closure_t closure,
-			    void *arg)
+static bool
+procutils_read_file (const char *filename, char *buffer, size_t buffer_size,
+		     procutils_closure_t closure, void *arg)
 {
-  enum { buffer_size = PROCUTILS_MAX_LINE_LEN };
-  char buffer[buffer_size];
   char *buffer_end = buffer + buffer_size;
   char *cp = buffer_end;
   char *re = buffer_end;
@@ -96,3 +93,23 @@  __libc_procutils_read_file (const char *filename,
 
   return r == 1;
 }
+
+
+bool
+__libc_procutils_read_file (const char *filename,
+			    procutils_closure_t closure,
+			    void *arg)
+{
+  enum { buffer_size = PROCUTILS_MAX_LINE_LEN };
+  char buffer[buffer_size];
+  return procutils_read_file (filename, buffer, buffer_size, closure, arg);
+}
+
+bool
+__libc_procutils_read_file_ext (const char *filename,
+				char *buffer, size_t size,
+				procutils_closure_t closure,
+				void *arg)
+{
+  return procutils_read_file (filename, buffer, size, closure, arg);
+}
diff --git a/sysdeps/unix/sysv/linux/procutils.h b/sysdeps/unix/sysv/linux/procutils.h
index acf1ec587a..e328ef5df4 100644
--- a/sysdeps/unix/sysv/linux/procutils.h
+++ b/sysdeps/unix/sysv/linux/procutils.h
@@ -20,6 +20,7 @@ 
 #define _PROCUTILS_H
 
 #include <stdbool.h>
+#include <stddef.h>
 
 typedef int (*procutils_closure_t) (const char *line, void *arg);
 
@@ -41,4 +42,11 @@  bool __libc_procutils_read_file (const char *filename,
 				 procutils_closure_t closure,
 				 void *arg) attribute_hidden;
 
+/* Same as __libc_procutils_read_file, but instead of using a stack allocated
+   buffer of PROCUTILS_MAX_LINE_LEN size, use BUFFER of SIZE instead.  */
+bool __libc_procutils_read_file_ext (const char *filename,
+				     char *buffer, size_t size,
+				     procutils_closure_t closure,
+				     void *arg) attribute_hidden;
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/readonly-area.c b/sysdeps/unix/sysv/linux/readonly-area.c
index 67975cf10d..adeec8aeaa 100644
--- a/sysdeps/unix/sysv/linux/readonly-area.c
+++ b/sysdeps/unix/sysv/linux/readonly-area.c
@@ -16,23 +16,77 @@ 
    <https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdio_ext.h>
+#include <procutils.h>
 #include <stdlib.h>
-#include <string.h>
-#include "libio/libioP.h"
+#include <stdint.h>
 
-/* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
-   Return -1 if it is writable.  */
+struct proc_self_find_map_t
+{
+  size_t size;
+  uintptr_t ptr;
+  uintptr_t ptr_end;
+};
+
+static int
+proc_self_find_map (const char *line, void *arg)
+{
+  struct proc_self_find_map_t *a = arg;
+
+  char *p;
+  uintptr_t from = strtoul (line, &p, 16);
+  if (p == line || *p++ != '-')
+    return -1;
+
+  char *q;
+  uintptr_t to = strtoul (p, &q, 16);
+  if (q == p || *q++ != ' ')
+    return -1;
+
+  if (from < a->ptr_end && to > a->ptr)
+    {
+      /* Found an entry that at least partially covers the area.  */
+      if (*q++ != 'r' || *q++ != '-')
+	return 1;
+
+      if (from <= a->ptr && to >= a->ptr_end)
+	{
+	  a->size = 0;
+	  return 1;
+	}
+      else if (from <= a->ptr)
+	a->size -= to - a->ptr;
+      else if (to >= a->ptr_end)
+	a->size -= a->ptr_end - from;
+      else
+	a->size -= to - from;
+
+      if (a->size == 0)
+	return 1;
+    }
+
+  return 0;
+}
 
 int
 __readonly_area (const char *ptr, size_t size)
 {
-  const void *ptr_end = ptr + size;
+  /* Each line is in the form:
 
-  FILE *fp = fopen ("/proc/self/maps", "rce");
-  if (fp == NULL)
+     hexnumber-hexnumber perms hexnumber dev inode pathname
+
+     with all fields up to pathname being aligned to 48 (32-bit architectures)
+     or 72 (64-bit architectures) characteres.  */
+  enum { buffer_size = 25 + sizeof(void *) * 6 - 1 + PATH_MAX };
+  char buffer[buffer_size];
+
+  struct proc_self_find_map_t args =
+    {
+      .size = size,
+      .ptr = (uintptr_t) ptr,
+      .ptr_end = (uintptr_t) ptr + size
+    };
+  if (!__libc_procutils_read_file_ext ("/proc/self/maps", buffer, buffer_size,
+				   proc_self_find_map,  &args))
     {
       /* It is the system administrator's choice to not have /proc
 	 available to this process (e.g., because it runs in a chroot
@@ -49,56 +103,7 @@  __readonly_area (const char *ptr, size_t size)
       return -1;
     }
 
-  /* We need no locking.  */
-  __fsetlocking (fp, FSETLOCKING_BYCALLER);
-
-  char *line = NULL;
-  size_t linelen = 0;
-
-  while (! __feof_unlocked (fp))
-    {
-      if (__getdelim (&line, &linelen, '\n', fp) <= 0)
-	break;
-
-      char *p;
-      uintptr_t from = strtoul (line, &p, 16);
-
-      if (p == line || *p++ != '-')
-	break;
-
-      char *q;
-      uintptr_t to = strtoul (p, &q, 16);
-
-      if (q == p || *q++ != ' ')
-	break;
-
-      if (from < (uintptr_t) ptr_end && to > (uintptr_t) ptr)
-	{
-	  /* Found an entry that at least partially covers the area.  */
-	  if (*q++ != 'r' || *q++ != '-')
-	    break;
-
-	  if (from <= (uintptr_t) ptr && to >= (uintptr_t) ptr_end)
-	    {
-	      size = 0;
-	      break;
-	    }
-	  else if (from <= (uintptr_t) ptr)
-	    size -= to - (uintptr_t) ptr;
-	  else if (to >= (uintptr_t) ptr_end)
-	    size -= (uintptr_t) ptr_end - from;
-	  else
-	    size -= to - from;
-
-	  if (!size)
-	    break;
-	}
-    }
-
-  fclose (fp);
-  free (line);
-
   /* If the whole area between ptr and ptr_end is covered by read-only
      VMAs, return 1.  Otherwise return -1.  */
-  return size == 0 ? 1 : -1;
+  return args.size == 0 ? 1 : -1;
 }