summaryrefslogtreecommitdiffstats
path: root/main/musl/1003-implement-PT_GNU_RELRO-support.patch
blob: 7557185060a3f9da1ae0253dd8053b3295fda569 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
From 2946056f1963aeb161568a7a8aea5b7bb9fb1edd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi>
Date: Tue, 25 Mar 2014 14:13:27 +0200
Subject: [PATCH 1/1] implement PT_GNU_RELRO support

---
 src/ldso/dynlink.c | 48 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 616dc3e..b903e97 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -74,6 +74,7 @@ struct dso {
 	char *rpath_orig, *rpath;
 	void *tls_image;
 	size_t tls_len, tls_size, tls_align, tls_id, tls_offset;
+	size_t relro_start, relro_end;
 	void **new_dtv;
 	unsigned char *new_tls;
 	int new_dtv_idx, new_tls_idx;
@@ -281,27 +282,29 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
  * and "donate" them to the heap by setting up minimal malloc
  * structures and then freeing them. */
 
-static void reclaim(unsigned char *base, size_t start, size_t end)
+static void reclaim(struct dso *dso, size_t start, size_t end)
 {
 	size_t *a, *z;
+	if (start >= dso->relro_start && start < dso->relro_end) start = dso->relro_end;
+	if (end   >= dso->relro_start && end   < dso->relro_end) end = dso->relro_start;
 	start = start + 6*sizeof(size_t)-1 & -4*sizeof(size_t);
 	end = (end & -4*sizeof(size_t)) - 2*sizeof(size_t);
 	if (start>end || end-start < 4*sizeof(size_t)) return;
-	a = (size_t *)(base + start);
-	z = (size_t *)(base + end);
+	a = (size_t *)(dso->base + start);
+	z = (size_t *)(dso->base + end);
 	a[-2] = 1;
 	a[-1] = z[0] = end-start + 2*sizeof(size_t) | 1;
 	z[1] = 1;
 	free(a);
 }
 
-static void reclaim_gaps(unsigned char *base, Phdr *ph, size_t phent, size_t phcnt)
+static void reclaim_gaps(struct dso *dso, Phdr *ph, size_t phent, size_t phcnt)
 {
 	for (; phcnt--; ph=(void *)((char *)ph+phent)) {
 		if (ph->p_type!=PT_LOAD) continue;
 		if ((ph->p_flags&(PF_R|PF_W))!=(PF_R|PF_W)) continue;
-		reclaim(base, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr);
-		reclaim(base, ph->p_vaddr+ph->p_memsz,
+		reclaim(dso, ph->p_vaddr & -PAGE_SIZE, ph->p_vaddr);
+		reclaim(dso, ph->p_vaddr+ph->p_memsz,
 			ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE);
 	}
 }
@@ -346,11 +349,14 @@ static void *map_library(int fd, struct dso *dso)
 	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
 		if (ph->p_type == PT_DYNAMIC)
 			dyn = ph->p_vaddr;
-		if (ph->p_type == PT_TLS) {
+		else if (ph->p_type == PT_TLS) {
 			tls_image = ph->p_vaddr;
 			dso->tls_align = ph->p_align;
 			dso->tls_len = ph->p_filesz;
 			dso->tls_size = ph->p_memsz;
+		} else if (ph->p_type == PT_GNU_RELRO) {
+			dso->relro_start = ph->p_vaddr & -PAGE_SIZE;
+			dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE;
 		}
 		if (ph->p_type != PT_LOAD) continue;
 		if (ph->p_vaddr < addr_min) {
@@ -419,12 +425,12 @@ static void *map_library(int fd, struct dso *dso)
 				goto error;
 			break;
 		}
-	if (!runtime) reclaim_gaps(base, ph0, eh->e_phentsize, eh->e_phnum);
 	dso->map = map;
 	dso->map_len = map_len;
 	dso->base = base;
 	dso->dynv = (void *)(base+dyn);
 	if (dso->tls_size) dso->tls_image = (void *)(base+tls_image);
+	if (!runtime) reclaim_gaps(dso, ph0, eh->e_phentsize, eh->e_phnum);
 	free(allocated_buf);
 	return map;
 noexec:
@@ -766,6 +772,17 @@ static void reloc_all(struct dso *p)
 			2+(dyn[DT_PLTREL]==DT_RELA));
 		do_relocs(p, (void *)(p->base+dyn[DT_REL]), dyn[DT_RELSZ], 2);
 		do_relocs(p, (void *)(p->base+dyn[DT_RELA]), dyn[DT_RELASZ], 3);
+
+		if (p->relro_start != p->relro_end &&
+		    mprotect(p->base+p->relro_start, p->relro_end-p->relro_start, PROT_READ) < 0) {
+			snprintf(errbuf, sizeof errbuf,
+				"Error relocating %s: RELRO protection failed",
+				p->name);
+			if (runtime) longjmp(*rtld_fail, 1);
+			dprintf(2, "%s\n", errbuf);
+			ldso_fail = 1;
+		}
+
 		p->relocated = 1;
 	}
 }
@@ -1027,6 +1044,9 @@ void *__dynlink(int argc, char **argv)
 				app->tls_len = phdr->p_filesz;
 				app->tls_size = phdr->p_memsz;
 				app->tls_align = phdr->p_align;
+			} else if (phdr->p_type == PT_GNU_RELRO) {
+				app->relro_start = phdr->p_vaddr & -PAGE_SIZE;
+				app->relro_end = (phdr->p_vaddr + phdr->p_memsz) & -PAGE_SIZE;
 			}
 		}
 		if (app->tls_size) app->tls_image = (char *)app->base + tls_image;
@@ -1133,9 +1153,9 @@ void *__dynlink(int argc, char **argv)
 	/* PAST THIS POINT, ALL LIBC INTERFACES ARE FULLY USABLE. */
 
 	/* Donate unused parts of app and library mapping to malloc */
-	reclaim_gaps(app->base, (void *)aux[AT_PHDR], aux[AT_PHENT], aux[AT_PHNUM]);
+	reclaim_gaps(app, (void *)aux[AT_PHDR], aux[AT_PHENT], aux[AT_PHNUM]);
 	ehdr = (void *)lib->base;
-	reclaim_gaps(lib->base, (void *)(lib->base+ehdr->e_phoff),
+	reclaim_gaps(lib, (void *)(lib->base+ehdr->e_phoff),
 		ehdr->e_phentsize, ehdr->e_phnum);
 
 	/* Load preload/needed libraries, add their symbols to the global
@@ -1175,9 +1195,11 @@ void *__dynlink(int argc, char **argv)
 	runtime = 1;
 
 #ifndef DYNAMIC_IS_RO
-	for (i=0; app->dynv[i]; i+=2)
-		if (app->dynv[i]==DT_DEBUG)
-			app->dynv[i+1] = (size_t)&debug;
+	if (app->relro_start == app->relro_end) {
+		for (i=0; app->dynv[i]; i+=2)
+			if (app->dynv[i]==DT_DEBUG)
+				app->dynv[i+1] = (size_t)&debug;
+	}
 #endif
 	debug.ver = 1;
 	debug.bp = _dl_debug_state;
-- 
1.9.0