summaryrefslogtreecommitdiff
path: root/libshouldbeinlibc
diff options
context:
space:
mode:
authorJustus Winter <justus@gnupg.org>2017-08-11 18:10:15 +0200
committerJustus Winter <justus@gnupg.org>2017-08-11 22:07:11 +0200
commit01ced1f5856659f4f457f39c270dd10f64ccaab8 (patch)
tree1b57c0aa31276f7b596d7eca20533a7017a8576a /libshouldbeinlibc
parente2823ba8f994292ecb92b83dfa3174e6f11c92fd (diff)
libshouldbeinlibc: Fix wiring code.
* libshouldbeinlibc/wire.c (statically_linked): New function. (loaded): Remove check. (wire_segment_internal): Fix error handling. Be smarter about the wiring, inspired by the mach-defpager code. (wire_segment): Fix error handling. (wire_task_self): Fix error handling. Distinguish between static binaries and dynamic binaries without DT_DEBUG or link map. When we don't have a map, fall back to wiring the whole space. Wire future allocations. * libshouldbeinlibc/wire.h (wire_task_self): Return error_t. (wire_segment): Likewise.
Diffstat (limited to 'libshouldbeinlibc')
-rw-r--r--libshouldbeinlibc/wire.c144
-rw-r--r--libshouldbeinlibc/wire.h4
2 files changed, 97 insertions, 51 deletions
diff --git a/libshouldbeinlibc/wire.c b/libshouldbeinlibc/wire.c
index ca5d32b1..5b41555c 100644
--- a/libshouldbeinlibc/wire.c
+++ b/libshouldbeinlibc/wire.c
@@ -24,6 +24,7 @@
#include <hurd.h>
#include <error.h>
#include <elf.h>
+#include <mach/gnumach.h>
#pragma weak _DYNAMIC
#pragma weak dlopen
@@ -34,15 +35,18 @@
#define RTLD_NOLOAD 0
#endif
+static int
+statically_linked (void)
+{
+ return &_DYNAMIC == 0; /* statically linked */
+}
+
/* Find the list of shared objects */
static struct link_map *
loaded (void)
{
ElfW(Dyn) *d;
- if (&_DYNAMIC == 0) /* statically linked */
- return 0;
-
for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d)
if (d->d_tag == DT_DEBUG)
{
@@ -79,7 +83,7 @@ map_extent (struct link_map *map)
/* Wire down all memory currently allocated at START for LEN bytes;
host_priv is the privileged host port. */
-static void
+static error_t
wire_segment_internal (vm_address_t start,
vm_size_t len,
host_priv_t host_priv)
@@ -101,40 +105,63 @@ wire_segment_internal (vm_address_t start,
err = vm_region (mach_task_self (), &addr, &size, &protection,
&max_protection, &inheritance, &shared, &object_name,
&offset);
+ if (err == KERN_NO_SPACE)
+ return 0; /* We're done. */
if (err)
- return;
-
- /* The current region begins at ADDR and is SIZE long. If it
- extends beyond the LEN, prune it. */
- if (addr + size > start + len)
- size = len - (addr - start);
-
- /* Set protection to allow all access possible */
- vm_protect (mach_task_self (), addr, size, 0, max_protection);
-
- /* Generate write faults */
- for (poke = (char *) addr;
- (vm_address_t) poke < addr + size;
- poke += vm_page_size)
- *poke = *poke;
-
- /* Wire pages */
- vm_wire (host_priv, mach_task_self (), addr, size, max_protection);
-
- /* Set protection back to what it was */
- vm_protect (mach_task_self (), addr, size, 0, protection);
-
-
+ return err;
mach_port_deallocate (mach_task_self (), object_name);
+ if (protection != VM_PROT_NONE)
+ {
+ /* The VM system cannot cope with a COW fault on another
+ unrelated virtual copy happening later when we have
+ wired down the original page. So we must touch all our
+ pages before wiring to make sure that only we will ever
+ use them. */
+
+ /* The current region begins at ADDR and is SIZE long. If it
+ extends beyond the LEN, prune it. */
+ if (addr + size > start + len)
+ size = len - (addr - start);
+
+ /* Set protection to allow all access possible */
+ if (!(protection & VM_PROT_WRITE))
+ {
+ err = vm_protect (mach_task_self (), addr, size, 0, max_protection);
+ if (err)
+ return err;
+ }
+
+ /* Generate write faults */
+ for (poke = (char *) addr;
+ (vm_address_t) poke < addr + size;
+ poke += vm_page_size)
+ *poke = *poke;
+
+ /* Wire pages */
+ err = vm_wire (host_priv, mach_task_self (), addr, size, protection);
+ if (err)
+ return err;
+
+ /* Set protection back to what it was */
+ if (!(protection & VM_PROT_WRITE))
+ {
+ err = vm_protect (mach_task_self (), addr, size, 0, protection);
+ if (err)
+ return err;
+ }
+ }
+
len -= (addr - start) + size;
start = addr + size;
}
while (len);
+
+ return err;
}
/* Wire down all memory currently allocated at START for LEN bytes. */
-void
+error_t
wire_segment (vm_address_t start,
vm_size_t len)
{
@@ -142,45 +169,64 @@ wire_segment (vm_address_t start,
error_t err;
err = get_privileged_ports (&host, &device);
- if (!err)
- {
- wire_segment_internal (start, len, host);
- mach_port_deallocate (mach_task_self (), host);
- mach_port_deallocate (mach_task_self (), device);
- }
-}
+ if (err)
+ return err;
+ err = wire_segment_internal (start, len, host);
+ mach_port_deallocate (mach_task_self (), host);
+ mach_port_deallocate (mach_task_self (), device);
+ return err;
+}
/* Wire down all the text and data (including from shared libraries)
for the current program. */
-void
+error_t
wire_task_self ()
{
- struct link_map *map;
mach_port_t host, device;
error_t err;
- extern char _edata, _etext, __data_start;
err = get_privileged_ports (&host, &device);
if (err)
- return;
+ return err;
- map = loaded ();
- if (!map)
+ if (statically_linked ())
{
extern void _start ();
+ extern char _edata, _etext, __data_start;
vm_address_t text_start = (vm_address_t) &_start;
- wire_segment_internal (text_start,
- (vm_size_t) (&_etext - text_start),
- host);
- wire_segment_internal ((vm_address_t) &__data_start,
- (vm_size_t) (&_edata - &__data_start),
- host);
+ err = wire_segment_internal (text_start,
+ (vm_size_t) (&_etext - text_start),
+ host);
+ if (err)
+ goto out;
+
+ err = wire_segment_internal ((vm_address_t) &__data_start,
+ (vm_size_t) (&_edata - &__data_start),
+ host);
}
else
- while (map)
- wire_segment ((vm_address_t) map->l_addr, map_extent (map));
+ {
+ struct link_map *map;
+
+ map = loaded ();
+ if (map)
+ for (err = 0; ! err && map; map = map->l_next)
+ err = wire_segment_internal ((vm_address_t) map->l_addr,
+ map_extent (map), host);
+ else
+ err = wire_segment_internal (VM_MIN_ADDRESS, VM_MAX_ADDRESS, host);
+ }
+
+ if (err)
+ goto out;
+
+ /* Automatically wire down future mappings, including those that are
+ currently PROT_NONE but become accessible. */
+ err = vm_wire_all (host, mach_task_self (), VM_WIRE_ALL);
+ out:
mach_port_deallocate (mach_task_self (), host);
mach_port_deallocate (mach_task_self (), device);
+ return err;
}
diff --git a/libshouldbeinlibc/wire.h b/libshouldbeinlibc/wire.h
index 6783cc50..2f310cf8 100644
--- a/libshouldbeinlibc/wire.h
+++ b/libshouldbeinlibc/wire.h
@@ -20,7 +20,7 @@
/* Wire down all the text and data (including from shared libraries)
for the current program. */
-void wire_task_self (void);
+error_t wire_task_self (void);
/* Wire down all memory currently allocated at START for LEN bytes. */
-void wire_segment (vm_address_t start, vm_size_t len);
+error_t wire_segment (vm_address_t start, vm_size_t len);