FORM 4.3
mallocprotect.h
1#ifndef MALLOCPROTECT_H
2#define MALLOCPROTECT_H
3
9
10/* #[ License : */
11/*
12 * Copyright (C) 1984-2022 J.A.M. Vermaseren
13 * When using this file you are requested to refer to the publication
14 * J.A.M.Vermaseren "New features of FORM" math-ph/0010025
15 * This is considered a matter of courtesy as the development was paid
16 * for by FOM the Dutch physics granting agency and we would like to
17 * be able to track its scientific use to convince FOM of its value
18 * for the community.
19 *
20 * This file is part of FORM.
21 *
22 * FORM is free software: you can redistribute it and/or modify it under the
23 * terms of the GNU General Public License as published by the Free Software
24 * Foundation, either version 3 of the License, or (at your option) any later
25 * version.
26 *
27 * FORM is distributed in the hope that it will be useful, but WITHOUT ANY
28 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
30 * details.
31 *
32 * You should have received a copy of the GNU General Public License along
33 * with FORM. If not, see <http://www.gnu.org/licenses/>.
34 */
35/* #] License : */
36
37/*
38 #[ Documentation :
39
40The enhanced malloc debugger. It intercepts access (both write AND
41read) to the memory outside the allocated chunk in the following
42manner: it prints out (to the stderr) the information about the event
43and goes to the spilock. The user is able to attach the debugger to
44the running process, investigate the problem and continue evaluation.
45
46In case of error, the debugger will print to the stderr the following
47information:
48"***** PID: ###, Addr: ###, signal ### (code ###)"
49"Attach gdb -p ### and set var loopForever=0 in a corresponding frame to continue"
50or for TFORM:
51"Attach gdb -p ### and set var loopForever=0 in a corresponding frame
52 of a thread with LPW id ### to continue"
53
54and go to the spilock. The user may attach gdb by the command
55gdb -p ### and type "where" (in case of TFORM, the user should first
56switch to the proper thread). Then investigation of the corresponding
57frames should clarify the situation. In order to continue the program,
58the user might set (in the corresponding frame of the corresponding
59thread) the variable loopForever to 0. The debugger will try to remove
60local problem lead to the exception.
61
62To activate the debugger, the macro MALLOCPROTECT should be
63defined. There are three possible values:
64#define MALLOCPROTECT -1 (any integer <0)
65#define MALLOCPROTECT 0
66#define MALLOCPROTECT 1 (any integer>0)
67
68If MALLOCPROTECT < 0, the debugger will intercept any access to the
69memory before the allocated chunk, even one byte.
70
71If MALLOCPROTECT == 0, the debugger will intercept any access to the
72memory before the allocated chunk, even one byte, and access to the
73memory page next to the allocated one.
74
75If MALLOCPROTECT > 0, the debugger will intercept any access to the
76memory after the allocated chunk, even one byte, and access to the
77memory page before the allocated one.
78
79The original FORM malloc debugger is able to catch errors when the
80allocated memory is freed improper, or if some small portion OUT of
81the allocated chunk is written. This debugger is a complementary
82one. It permits to catch situation when some small portion of memory
83before or after the allocated chunk is written or even just read.
84
85The idea is to protect the beginning or the end of the allocated chunk
86for any kind of access and install the SIGSEGV handler in order to
87intercept the access to this memory. Moreover, the allocated memory is
88always immediately returned to the system so if the user tries to
89access the memory after it is freed then the handler is triggered,
90also.
91
92The problem here is that we are able to allocate / protec only the
93whole page. So the user has to run the debugger twice: one time with
94the left alignment (#define MALLOCPROTECT <0), and then with the rigth
95alignment (#define MALLOCPROTECT >0). During the first run the possible
96errors like x[-1] will be catched, and the second run will manifest
97reading over the allocated ares.
98
99The leftmost extra page is always allocated and mprotected. The size
100of the allocated chunk is stored in the beginning of this page.
101
102 #] Documentation :
103 #[ Includes :
104*/
105
106#include <stdio.h>
107#include <stdlib.h>
108#include <unistd.h>
109#include <signal.h>
110#include <sys/mman.h>
111
112#ifdef WITHPTHREADS
113#ifdef LINUX
114#include <sys/syscall.h>
115#endif
116#endif
117
118/*
119 #] Includes :
120*/
121
122static int pageSize=4096;/*the default value*/
123
124
125/*
126 #[ segv_handler :
127*/
128
129/*The handler will be invoked on some signal (usually SIGSEGV). It
130will print some diagnostics to stderr and hands up in infinite loop
131waiting the user for debugging:*/
132static void segv_handler(int sig, siginfo_t *sip, void *xxx) {
133 char *vadr;
134 char *errStr;
135 int actionBeforeExit=0;/* < 0 - unprotect, > 0 - map */
136 switch(sip->si_signo){/* All POSIX signals for which the field siginfo_t.si_addr is defined:*/
137 case SIGILL:
138 switch(sip->si_code){
139 case ILL_ILLOPC:
140 errStr="SIGILL: Illegal opcode";
141 break;
142 case ILL_ILLOPN:
143 errStr="SIGILL: Illegal operand";
144 break;
145 case ILL_ILLADR:
146 errStr="SIGILL: Illegal addressing mode";
147 break;
148 case ILL_ILLTRP:
149 errStr="SIGILL: Illegal trap";
150 break;
151 case ILL_PRVOPC:
152 errStr="SIGILL: Privileged opcode";
153 break;
154 case ILL_PRVREG:
155 errStr="SIGILL: Privileged register";
156 break;
157 case ILL_COPROC:
158 errStr="SIGILL: Coprocessor error";
159 break;
160 case ILL_BADSTK:
161 errStr="SIGILL: Internal stack error";
162 break;
163 default:
164 errStr="SIGILL: Unknown signal code";
165 }/*case SIGILL:*/
166 break;
167 case SIGFPE:
168 switch(sip->si_code){
169 case FPE_INTDIV:
170 errStr="SIGFPE: Integer divide-by-zero";
171 break;
172 case FPE_INTOVF:
173 errStr="SIGFPE: Integer overflow";
174 break;
175 case FPE_FLTDIV:
176 errStr="SIGFPE: Floating point divide-by-zero";
177 break;
178 case FPE_FLTOVF:
179 errStr="SIGFPE: Floating point overflow";
180 break;
181 case FPE_FLTUND:
182 errStr="SIGFPE: Floating point underflow";
183 break;
184 case FPE_FLTRES:
185 errStr="SIGFPE: Floating point inexact result";
186 break;
187 case FPE_FLTINV:
188 errStr="SIGFPE: Invalid floating point operation";
189 break;
190 case FPE_FLTSUB:
191 errStr="SIGFPE: Subscript out of range";
192 break;
193 default:
194 errStr="SIGFPE: Unknown signal code";
195 }/*switch(sip->si_code)*/
196 break;
197 case SIGSEGV:
198 switch(sip->si_code){
199 case SEGV_MAPERR:
200 errStr="SIGSEGV: Address not mapped";
201 actionBeforeExit = 1;
202 break;
203 case SEGV_ACCERR:
204 errStr="SIGSEGV: Invalid permissions";
205 actionBeforeExit = -1;
206 break;
207 default:
208 errStr="SIGSEGV: Unknown signal code";
209 }/*switch(sip->si_code)*/
210 break;
211 case SIGBUS:
212 switch(sip->si_code){
213 case BUS_ADRALN:
214 errStr="SIGBUS: Invalid address alignment";
215 break;
216 case BUS_ADRERR:
217 errStr="SIGBUS: Non-existent physical address";
218 break;
219 case BUS_OBJERR:
220 errStr="SIGBUS: Object-specific hardware error";
221 break;
222 default:
223 errStr="SIGBUS: Unknown signal code";
224 }/*switch(sip->si_code)*/
225 break;
226 default:
227 errStr="Unknown signal";
228 }/*switch(sip->si_signo)*/
229
230 vadr = (caddr_t)sip->si_addr;
231 fprintf(stderr, "\n***** PID: %ld, Addr: %p, signal %s (code %d)\n",
232 (long)getpid(), vadr, errStr,sip->si_code);
233
234 {/*Block*/
235 /*The process hangs up at this block.
236 Attach gdb to investigate and continue:
237 */
238 volatile int loopForever=1;
239 size_t alignedAdr=((size_t)vadr) & (~(pageSize-1));
240 fprintf(stderr, " Attach gdb -p %ld and set var loopForever=0 in a corresponding frame",
241 (long)getpid());
242#ifdef CORRECTCODE
243#ifdef WITHPTHREADS
244#ifdef LINUX
245 fprintf(stderr, "\n of a thread with LPW id %ld",(long)syscall(SYS_gettid));
246#else
247 /*If the compiler fails here, just remove the next line:*/
248 fprintf(stderr, "\n of thread with LPW id %ld",(long)gettid());
249#endif
250#endif
251#endif
252 fprintf(stderr, " to continue\n");
253
254
255 while(loopForever)
256 sleep(1);
257
258 if(actionBeforeExit<0)/*After changing loopForever=0, unprotect the page to continue:*/
259 mprotect((char*)alignedAdr, pageSize, PROT_READ | PROT_WRITE);
260 if(actionBeforeExit>0)/*After changing loopForever=0, map the page to continue:*/
261/* mmap((void*)alignedAdr,pageSize,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); */
262 mmap((void*)alignedAdr,pageSize,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, 0, 0);
263 }/*Block*/
264}/*segv_handler*/
265
266/*
267 #] segv_handler :
268*/
269
270/*
271 #[ mprotectInit :
272*/
273
274static FORM_INLINE int mprotectInit(void)
275{
276 struct sigaction sa;
277 pageSize = getpagesize();
278 sa.sa_sigaction = &segv_handler;
279
280 sigemptyset(&sa.sa_mask);
281 sigaddset(&sa.sa_mask, SIGIO);
282 sigaddset(&sa.sa_mask, SIGALRM);
283
284 sa.sa_flags = SA_SIGINFO;
285
286 if (sigaction(SIGILL, &sa, NULL)) {
287 fprintf(stderr,"Error on assigning %s.\n","SIGILL");
288 return SIGILL;
289 }
290 if (sigaction(SIGFPE, &sa, NULL)) {
291 fprintf(stderr,"Error on assigning %s.\n","SIGFPE");
292 return SIGFPE;
293 }
294 if (sigaction(SIGSEGV, &sa, NULL)) {
295 fprintf(stderr,"Error on assigning %s.\n","SIGSEGV");
296 return SIGSEGV;
297 }
298 if (sigaction(SIGBUS, &sa, NULL)) {
299 fprintf(stderr,"Error on assigning %s.\n","SIGBUS");
300 return SIGBUS;
301 }
302 return 0;
303}/*mprotectInit*/
304
305/*
306 #] mprotectInit :
307*/
308
309
310/*
311 #[ mprotectMalloc :
312*/
313
314static void *mprotectMalloc(size_t theSize)
315{
316#if MALLOCPROTECT < 0
317 /*Only one side is protected*/
318 size_t nPages=1;
319#else
320 /*Both sides are protected*/
321 size_t nPages=2;
322#if MALLOCPROTECT > 0
323 /*will need the original theSize*/
324 size_t requestedSize=theSize;
325#endif
326#endif
327
328 char *ret=NULL;
329 size_t *ptr;
330
331
332
333 /*Align required size to the pagesize:*/
334 if(theSize % pageSize)
335 nPages++;
336 theSize= (theSize/pageSize+nPages)*pageSize;
337
338/* ret=(char*)mmap(0,theSize,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0); */
339 ret=(char*)mmap(0,theSize,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, 0, 0);
340 if(ret == MAP_FAILED)
341 return NULL;
342 ptr=(size_t *)ret;
343 *ptr=theSize;
344 if(mprotect(ret, pageSize, PROT_NONE))return NULL;
345#if MALLOCPROTECT < 0
346 return ret+pageSize;
347#else
348 if(mprotect(ret+(theSize-pageSize), pageSize, PROT_NONE))return NULL;
349#if MALLOCPROTECT ==0
350 return ret+pageSize;
351#endif
352#endif
353 /*MALLOCPROTECT > 0, but we need conditional compilation
354 since the variable requestedSize:*/
355#if MALLOCPROTECT > 0
356
357 /*Potential problems with alignment if the requested size is not
358 a multiple of items. But no poblems on x86-64:*/
359 return ret+ (theSize-pageSize-requestedSize);
360#endif
361}/*mprotectMalloc*/
362/*
363 #] mprotectMalloc :
364*/
365
366/*
367 #[ mprotectFree :
368*/
369
370static void mprotectFree(char *ptr)
371{
372 size_t theSize;
373 if(ptr==NULL) return;
374#if MALLOCPROTECT > 0
375 /*The memory block was moved to the right, find the left page boundary:*/
376 {/*Block*/
377 size_t alignedPtr=((size_t)ptr) & (~(pageSize-1));
378 ptr=(char*)alignedPtr;
379 }/*Block*/
380#endif
381
382 ptr-=pageSize;
383 mprotect(ptr, pageSize, PROT_READ);
384 theSize=*((size_t*)ptr);
385 munmap(ptr,theSize);
386}/*mprotectFree*/
387
388/*
389 #] mprotectFree :
390*/
391
392#endif