Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/osl/unx/signal.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <signalshared.hxx>
23
24
#include <config_features.h>
25
26
#include "soffice.hxx"
27
28
#include "backtrace.h"
29
30
0
#define MAX_STACK_FRAMES 256
31
32
#include <osl/diagnose.h>
33
#include <osl/signal.h>
34
#include <sal/log.hxx>
35
#include <sal/macros.h>
36
#include <sal/backtrace.hxx>
37
38
0
#define ACT_IGNORE  1
39
0
#define ACT_EXIT    2
40
2.91k
#define ACT_SYSTEM  3
41
1.62k
#define ACT_HIDE    4
42
0
#define ACT_ABORT   5
43
44
#if defined HAVE_VALGRIND_HEADERS
45
#include <valgrind/memcheck.h>
46
#endif
47
48
#include <signal.h>
49
#include <unistd.h>
50
51
namespace
52
{
53
extern "C" using Handler1_t = void (*)(int);
54
extern "C" using Handler2_t = void (*)(int, siginfo_t *, void *);
55
struct SignalAction
56
{
57
    int Signal;
58
    int Action;
59
    union {
60
        Handler1_t Handler1;
61
        Handler2_t Handler2;
62
    };
63
    bool siginfo; // Handler2 is active
64
} Signals[] =
65
{
66
    { SIGHUP,    ACT_HIDE,   SIG_DFL, false }, /* hangup */
67
    { SIGINT,    ACT_EXIT,   SIG_DFL, false }, /* interrupt (rubout) */
68
    { SIGQUIT,   ACT_EXIT,   SIG_DFL, false }, /* quit (ASCII FS) */
69
    { SIGILL,    ACT_SYSTEM, SIG_DFL, false }, /* illegal instruction (not reset when caught) */
70
/* changed from ACT_ABOUT to ACT_SYSTEM to try and get collector to run*/
71
    { SIGTRAP,   ACT_ABORT,  SIG_DFL, false }, /* trace trap (not reset when caught) */
72
#if ( SIGIOT != SIGABRT )
73
    { SIGIOT,    ACT_ABORT,  SIG_DFL, false }, /* IOT instruction */
74
#endif
75
#if defined(FORCE_DEFAULT_SIGNAL)
76
    { SIGABRT,   ACT_SYSTEM, SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
77
#else
78
    { SIGABRT,   ACT_ABORT,  SIG_DFL, false }, /* used by abort, replace SIGIOT in the future */
79
#endif
80
#ifdef SIGEMT
81
    { SIGEMT,    ACT_SYSTEM, SIG_DFL, false }, /* EMT instruction */
82
/* changed from ACT_ABORT to ACT_SYSTEM to remove handler*/
83
/* SIGEMT may also be used by the profiler - so it is probably not a good
84
plan to have the new handler use this signal*/
85
#endif
86
    { SIGFPE,    ACT_ABORT,  SIG_DFL, false }, /* floating point exception */
87
    { SIGKILL,   ACT_SYSTEM, SIG_DFL, false }, /* kill (cannot be caught or ignored) */
88
    { SIGBUS,    ACT_ABORT,  SIG_DFL, false }, /* bus error */
89
#if defined(FORCE_DEFAULT_SIGNAL)
90
    { SIGSEGV,   ACT_SYSTEM, SIG_DFL, false }, /* segmentation violation */
91
#else
92
    { SIGSEGV,   ACT_ABORT,  SIG_DFL, false }, /* segmentation violation */
93
#endif
94
#ifdef SIGSYS
95
    { SIGSYS,    ACT_ABORT,  SIG_DFL, false }, /* bad argument to system call */
96
#endif
97
    { SIGPIPE,   ACT_HIDE,   SIG_DFL, false }, /* write on a pipe with no one to read it */
98
#if defined(FORCE_DEFAULT_SIGNAL)
99
    { SIGALRM,   ACT_SYSTEM, SIG_DFL, false }, /* alarm clock */
100
#else
101
    { SIGALRM,   ACT_EXIT,   SIG_DFL, false }, /* alarm clock */
102
#endif
103
    { SIGTERM,   ACT_EXIT,   SIG_DFL, false }, /* software termination signal from kill */
104
    { SIGUSR1,   ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 1 */
105
    { SIGUSR2,   ACT_SYSTEM, SIG_DFL, false }, /* user defined signal 2 */
106
    { SIGCHLD,   ACT_SYSTEM, SIG_DFL, false }, /* child status change */
107
#ifdef SIGPWR
108
    { SIGPWR,    ACT_IGNORE, SIG_DFL, false }, /* power-fail restart */
109
#endif
110
    { SIGWINCH,  ACT_IGNORE, SIG_DFL, false }, /* window size change */
111
    { SIGURG,    ACT_EXIT,   SIG_DFL, false }, /* urgent socket condition */
112
#ifdef SIGPOLL
113
    { SIGPOLL,   ACT_EXIT,   SIG_DFL, false }, /* pollable event occurred */
114
#endif
115
    { SIGSTOP,   ACT_SYSTEM, SIG_DFL, false }, /* stop (cannot be caught or ignored) */
116
    { SIGTSTP,   ACT_SYSTEM, SIG_DFL, false }, /* user stop requested from tty */
117
    { SIGCONT,   ACT_SYSTEM, SIG_DFL, false }, /* stopped process has been continued */
118
    { SIGTTIN,   ACT_SYSTEM, SIG_DFL, false }, /* background tty read attempted */
119
    { SIGTTOU,   ACT_SYSTEM, SIG_DFL, false }, /* background tty write attempted */
120
    { SIGVTALRM, ACT_EXIT,   SIG_DFL, false }, /* virtual timer expired */
121
    { SIGPROF,   ACT_SYSTEM, SIG_DFL, false }, /* profiling timer expired */
122
/*Change from ACT_EXIT to ACT_SYSTEM for SIGPROF is so that profiling signals do
123
not get taken by the new handler - the new handler does not pass on context
124
information which causes 'collect' to crash. This is a way of avoiding
125
what looks like a bug in the new handler*/
126
    { SIGXCPU,   ACT_ABORT,  SIG_DFL, false }, /* exceeded cpu limit */
127
    { SIGXFSZ,   ACT_ABORT,  SIG_DFL, false }  /* exceeded file size limit */
128
};
129
const auto NoSignals = std::ssize(Signals);
130
131
bool bSetSEGVHandler = false;
132
bool bSetWINCHHandler = false;
133
bool bSetILLHandler = false;
134
135
void signalHandlerFunction(int, siginfo_t *, void *);
136
137
#if HAVE_FEATURE_BREAKPAD
138
bool is_unset_signal(int signal)
139
{
140
#ifdef DBG_UTIL
141
    return (!bSetSEGVHandler && signal == SIGSEGV) ||
142
        (!bSetWINCHHandler && signal == SIGWINCH) ||
143
        (!bSetILLHandler && signal == SIGILL);
144
#else
145
    (void) signal;
146
    return false;
147
#endif
148
}
149
#endif
150
151
}
152
153
bool onInitSignal()
154
108
{
155
108
    if (sal::detail::isSoffice())
156
0
    {
157
        // WORKAROUND FOR SEGV HANDLER CONFLICT
158
        //
159
        // the java jit needs SIGSEGV for proper work
160
        // and we need SIGSEGV for the office crashguard
161
        //
162
        // TEMPORARY SOLUTION:
163
        //   the office sets the signal handler during startup
164
        //   java can than overwrite it, if needed
165
0
        bSetSEGVHandler = true;
166
167
        // WORKAROUND FOR WINCH HANDLER (SEE ABOVE)
168
0
        bSetWINCHHandler = true;
169
170
        // WORKAROUND FOR ILLEGAL INSTRUCTION HANDLER (SEE ABOVE)
171
0
        bSetILLHandler = true;
172
0
    }
173
174
#ifdef DBG_UTIL
175
    bSetSEGVHandler = bSetWINCHHandler = bSetILLHandler = false;
176
#endif
177
178
108
    struct sigaction act;
179
108
    act.sa_sigaction = signalHandlerFunction;
180
108
    act.sa_flags = SA_RESTART | SA_SIGINFO;
181
182
108
    sigfillset(&(act.sa_mask));
183
184
    /* Initialize the rest of the signals */
185
108
    for (SignalAction & rSignal : Signals)
186
3.24k
    {
187
#if defined HAVE_VALGRIND_HEADERS
188
        if (rSignal.Signal == SIGUSR2 && RUNNING_ON_VALGRIND)
189
            rSignal.Action = ACT_IGNORE;
190
#endif
191
192
        /* hack: stomcatd is attaching JavaVM which does not work with an sigaction(SEGV) */
193
3.24k
        if ((bSetSEGVHandler || rSignal.Signal != SIGSEGV)
194
3.13k
        && (bSetWINCHHandler || rSignal.Signal != SIGWINCH)
195
3.02k
        && (bSetILLHandler   || rSignal.Signal != SIGILL))
196
2.91k
        {
197
2.91k
            if (rSignal.Action != ACT_SYSTEM)
198
1.62k
            {
199
1.62k
                if (rSignal.Action == ACT_HIDE)
200
216
                {
201
216
                    struct sigaction ign;
202
203
216
                    ign.sa_handler = SIG_IGN;
204
216
                    ign.sa_flags   = 0;
205
216
                    sigemptyset(&ign.sa_mask);
206
207
216
                    struct sigaction oact;
208
216
                    if (sigaction(rSignal.Signal, &ign, &oact) == 0) {
209
216
                        rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
210
216
                        if (rSignal.siginfo) {
211
0
                            rSignal.Handler2 =
212
0
                                oact.sa_sigaction;
213
216
                        } else {
214
216
                            rSignal.Handler1 = oact.sa_handler;
215
216
                        }
216
216
                    } else {
217
0
                        rSignal.Handler1 = SIG_DFL;
218
0
                        rSignal.siginfo = false;
219
0
                    }
220
216
                }
221
1.40k
                else
222
1.40k
                {
223
1.40k
                    struct sigaction oact;
224
1.40k
                    if (sigaction(rSignal.Signal, &act, &oact) == 0) {
225
1.40k
                        rSignal.siginfo = (oact.sa_flags & SA_SIGINFO) != 0;
226
1.40k
                        if (rSignal.siginfo) {
227
216
                            rSignal.Handler2 =
228
216
                                oact.sa_sigaction;
229
1.18k
                        } else {
230
1.18k
                            rSignal.Handler1 = oact.sa_handler;
231
1.18k
                        }
232
1.40k
                    } else {
233
0
                        rSignal.Handler1 = SIG_DFL;
234
0
                        rSignal.siginfo = false;
235
0
                    }
236
1.40k
                }
237
1.62k
            }
238
2.91k
        }
239
3.24k
    }
240
241
    /* Clear signal mask inherited from parent process (on macOS, upon a
242
       crash soffice re-execs itself from within the signal handler, so the
243
       second soffice would have the guilty signal blocked and would freeze upon
244
       encountering a similar crash again): */
245
108
    sigset_t unset;
246
108
    if (sigemptyset(&unset) < 0 ||
247
108
        pthread_sigmask(SIG_SETMASK, &unset, nullptr) < 0)
248
0
    {
249
0
        SAL_WARN("sal.osl", "sigemptyset or pthread_sigmask failed");
250
0
    }
251
252
108
    return true;
253
108
}
254
255
bool onDeInitSignal()
256
0
{
257
0
    struct sigaction act;
258
259
0
    sigemptyset(&(act.sa_mask));
260
261
    /* Initialize the rest of the signals */
262
0
    for (int i = NoSignals - 1; i >= 0; i--)
263
0
        if (Signals[i].Action != ACT_SYSTEM
264
0
            && ((bSetSEGVHandler || Signals[i].Signal != SIGSEGV)
265
0
                && (bSetWINCHHandler || Signals[i].Signal != SIGWINCH)
266
0
                && (bSetILLHandler || Signals[i].Signal != SIGILL)))
267
0
        {
268
0
            if (Signals[i].siginfo) {
269
0
                act.sa_sigaction =
270
0
                    Signals[i].Handler2;
271
0
                act.sa_flags = SA_SIGINFO;
272
0
            } else {
273
0
                act.sa_handler = Signals[i].Handler1;
274
0
                act.sa_flags = 0;
275
0
            }
276
277
0
            sigaction(Signals[i].Signal, &act, nullptr);
278
0
        }
279
280
0
    return false;
281
0
}
282
283
namespace
284
{
285
void printStack(int sig)
286
0
{
287
0
    std::unique_ptr<sal::BacktraceState> bs = sal::backtrace_get(MAX_STACK_FRAMES);
288
289
0
    fprintf( stderr, "\n\nFatal exception: Signal %d\n", sig );
290
291
#if ! HAVE_FEATURE_BACKTRACE && defined( MACOSX ) && !defined( INTEL )
292
    fprintf( stderr, "Please turn on Enable Crash Reporting and\nAutomatic Display of Crashlogs in the Console application\n" );
293
#endif
294
295
0
    fputs( "Stack:\n", stderr );
296
0
    fprintf( stderr, "%s\n", OUStringToOString( sal::backtrace_to_string(bs.get()), RTL_TEXTENCODING_UTF8 ).getStr() );
297
0
}
298
299
void callSystemHandler(int signal, siginfo_t * info, void * context)
300
0
{
301
0
    int i;
302
303
0
    for (i = 0; i < NoSignals; i++)
304
0
    {
305
0
        if (Signals[i].Signal == signal)
306
0
            break;
307
0
    }
308
309
0
    if (i >= NoSignals)
310
0
        return;
311
312
0
    if ((Signals[i].Handler1 == SIG_DFL) ||
313
0
        (Signals[i].Handler1 == SIG_IGN) ||
314
0
         (Signals[i].Handler1 == SIG_ERR))
315
0
    {
316
0
        switch (Signals[i].Action)
317
0
        {
318
0
            case ACT_EXIT:      /* terminate */
319
                /* prevent dumping core on exit() */
320
0
                _exit(255);
321
0
                break;
322
323
0
            case ACT_ABORT:     /* terminate with core dump */
324
0
                struct sigaction act;
325
0
                act.sa_handler = SIG_DFL;
326
0
                act.sa_flags   = 0;
327
0
                sigemptyset(&(act.sa_mask));
328
0
                sigaction(SIGABRT, &act, nullptr);
329
0
                printStack( signal );
330
0
                abort();
331
0
                break;
332
333
0
            case ACT_IGNORE:    /* ignore */
334
0
                break;
335
336
0
            default:            /* should never happen */
337
0
                OSL_ASSERT(false);
338
0
        }
339
0
    }
340
0
    else if (Signals[i].siginfo) {
341
0
        (*Signals[i].Handler2)(
342
0
            signal, info, context);
343
0
    } else {
344
0
        (*Signals[i].Handler1)(signal);
345
0
    }
346
0
}
347
348
#if defined HAVE_VALGRIND_HEADERS
349
void DUMPCURRENTALLOCS()
350
{
351
    VALGRIND_PRINTF( "=== start memcheck dump of active allocations ===\n" );
352
353
#if __GNUC__ && !defined(__clang__)
354
#   pragma GCC diagnostic push
355
#   pragma GCC diagnostic ignored "-Wunused-but-set-variable"
356
#endif
357
358
    VALGRIND_DO_LEAK_CHECK;
359
360
#if __GNUC__ && !defined(__clang__)
361
#   pragma GCC diagnostic pop
362
#endif
363
364
    VALGRIND_PRINTF( "=== end memcheck dump of active allocations ===\n" );
365
}
366
#endif
367
368
void signalHandlerFunction(int signal, siginfo_t * info, void * context)
369
0
{
370
0
    oslSignalInfo Info;
371
372
0
    Info.UserSignal = signal;
373
0
    Info.UserData   = nullptr;
374
375
0
    switch (signal)
376
0
    {
377
0
        case SIGBUS:
378
0
        case SIGILL:
379
0
        case SIGSEGV:
380
0
        case SIGIOT:
381
#if ( SIGIOT != SIGABRT )
382
        case SIGABRT:
383
#endif
384
0
            Info.Signal = osl_Signal_AccessViolation;
385
0
            break;
386
387
0
        case -1:
388
0
            Info.Signal = osl_Signal_IntegerDivideByZero;
389
0
            break;
390
391
0
        case SIGFPE:
392
0
            Info.Signal = osl_Signal_FloatDivideByZero;
393
0
            break;
394
395
0
        case SIGINT:
396
0
        case SIGTERM:
397
0
        case SIGQUIT:
398
0
            Info.Signal = osl_Signal_Terminate;
399
0
            break;
400
401
#if defined HAVE_VALGRIND_HEADERS
402
        case SIGUSR2:
403
            if (RUNNING_ON_VALGRIND)
404
                DUMPCURRENTALLOCS();
405
            Info.Signal = osl_Signal_System;
406
            break;
407
#endif
408
409
0
        default:
410
0
            Info.Signal = osl_Signal_System;
411
0
            break;
412
0
    }
413
414
#if HAVE_FEATURE_BREAKPAD
415
    if ((Info.Signal == osl_Signal_AccessViolation ||
416
            Info.Signal == osl_Signal_IntegerDivideByZero ||
417
            Info.Signal == osl_Signal_FloatDivideByZero) && !is_unset_signal(signal))
418
    {
419
        callSystemHandler(signal, info, context);
420
    }
421
#endif
422
423
0
    switch (callSignalHandler(&Info))
424
0
    {
425
0
    case osl_Signal_ActCallNextHdl:
426
0
        callSystemHandler(signal, info, context);
427
0
        break;
428
429
0
    case osl_Signal_ActAbortApp:
430
0
        struct sigaction act;
431
0
        act.sa_handler = SIG_DFL;
432
0
        act.sa_flags   = 0;
433
0
        sigemptyset(&(act.sa_mask));
434
0
        sigaction(SIGABRT, &act, nullptr);
435
0
        printStack( signal );
436
0
        abort();
437
0
        break;
438
439
0
    case osl_Signal_ActKillApp:
440
        /* prevent dumping core on exit() */
441
0
        _exit(255);
442
0
        break;
443
0
    default:
444
0
        break;
445
0
    }
446
0
}
447
448
}
449
450
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */