(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
readonly-area.c
       1  /* Copyright (C) 2004-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <stdint.h>
      20  #include <stdio.h>
      21  #include <stdio_ext.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include "libio/libioP.h"
      25  
      26  /* Return 1 if the whole area PTR .. PTR+SIZE is not writable.
      27     Return -1 if it is writable.  */
      28  
      29  int
      30  __readonly_area (const char *ptr, size_t size)
      31  {
      32    const void *ptr_end = ptr + size;
      33  
      34    FILE *fp = fopen ("/proc/self/maps", "rce");
      35    if (fp == NULL)
      36      {
      37        /* It is the system administrator's choice to not have /proc
      38  	 available to this process (e.g., because it runs in a chroot
      39  	 environment.  Don't fail in this case.  */
      40        if (errno == ENOENT
      41  	  /* The kernel has a bug in that a process is denied access
      42  	     to the /proc filesystem if it is set[ug]id.  There has
      43  	     been no willingness to change this in the kernel so
      44  	     far.  */
      45  	  || errno == EACCES)
      46  	return 1;
      47        return -1;
      48      }
      49  
      50    /* We need no locking.  */
      51    __fsetlocking (fp, FSETLOCKING_BYCALLER);
      52  
      53    char *line = NULL;
      54    size_t linelen = 0;
      55  
      56    while (! __feof_unlocked (fp))
      57      {
      58        if (__getdelim (&line, &linelen, '\n', fp) <= 0)
      59  	break;
      60  
      61        char *p;
      62        uintptr_t from = strtoul (line, &p, 16);
      63  
      64        if (p == line || *p++ != '-')
      65  	break;
      66  
      67        char *q;
      68        uintptr_t to = strtoul (p, &q, 16);
      69  
      70        if (q == p || *q++ != ' ')
      71  	break;
      72  
      73        if (from < (uintptr_t) ptr_end && to > (uintptr_t) ptr)
      74  	{
      75  	  /* Found an entry that at least partially covers the area.  */
      76  	  if (*q++ != 'r' || *q++ != '-')
      77  	    break;
      78  
      79  	  if (from <= (uintptr_t) ptr && to >= (uintptr_t) ptr_end)
      80  	    {
      81  	      size = 0;
      82  	      break;
      83  	    }
      84  	  else if (from <= (uintptr_t) ptr)
      85  	    size -= to - (uintptr_t) ptr;
      86  	  else if (to >= (uintptr_t) ptr_end)
      87  	    size -= (uintptr_t) ptr_end - from;
      88  	  else
      89  	    size -= to - from;
      90  
      91  	  if (!size)
      92  	    break;
      93  	}
      94      }
      95  
      96    fclose (fp);
      97    free (line);
      98  
      99    /* If the whole area between ptr and ptr_end is covered by read-only
     100       VMAs, return 1.  Otherwise return -1.  */
     101    return size == 0 ? 1 : -1;
     102  }