this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <signal.h>
3#include <sys/resource.h>
4#include <unistd.h>
5
6#include <cerrno>
7#include <csignal>
8
9#include "builtins.h"
10#include "file.h"
11#include "int-builtins.h"
12#include "modules.h"
13#include "runtime.h"
14#include "sys-module.h"
15
16namespace py {
17
18struct FaultHandler {
19 int signum;
20 const char* msg;
21 bool enabled;
22 struct sigaction previous_handler;
23};
24
25static struct {
26 int fd;
27 bool all_threads;
28 bool enabled;
29 Runtime* runtime;
30} fatal_error = {-1};
31
32static FaultHandler handler_sigabrt = {SIGABRT, "Aborted"};
33static FaultHandler handler_sigbus = {SIGBUS, "Bus error"};
34static FaultHandler handler_sigfpe = {SIGFPE, "Floating point exception"};
35static FaultHandler handler_sigill = {SIGILL, "Illegal instruction"};
36static FaultHandler handler_sigsegv = {SIGSEGV, "Segmentation fault"};
37
38static void disableFatalHandler(FaultHandler* handler) {
39 if (!handler->enabled) return;
40 handler->enabled = false;
41 int result =
42 ::sigaction(handler->signum, &handler->previous_handler, nullptr);
43 DCHECK(result == 0, "sigaction returned unexpected error");
44}
45
46static RawObject getFileno(Thread* thread, const Object& file) {
47 Runtime* runtime = thread->runtime();
48 if (file.isNoneType()) {
49 return SmallInt::fromWord(File::kStderr);
50 }
51 if (runtime->isInstanceOfInt(*file)) {
52 RawInt fd = intUnderlying(*file);
53 if (fd.isNegative() || fd.isLargeInt()) {
54 return thread->raiseWithFmt(LayoutId::kValueError,
55 "file is not a valid file descriptor");
56 }
57 return fd.isSmallInt() ? fd : convertBoolToInt(fd);
58 }
59
60 HandleScope scope(thread);
61 Object fileno(&scope, thread->invokeMethod1(file, ID(fileno)));
62 if (fileno.isError()) {
63 if (fileno.isErrorNotFound()) {
64 return thread->raiseWithFmt(LayoutId::kAttributeError,
65 "'%T' object has no attribute 'fileno'",
66 &file);
67 }
68 return *fileno;
69 }
70
71 if (!runtime->isInstanceOfInt(*fileno)) {
72 return thread->raiseWithFmt(LayoutId::kRuntimeError,
73 "file.fileno() is not a valid file descriptor");
74 }
75 Int fd(&scope, intUnderlying(*fileno));
76 if (fd.isNegative() || fd.isLargeInt()) {
77 return thread->raiseWithFmt(LayoutId::kRuntimeError,
78 "file.fileno() is not a valid file descriptor");
79 }
80
81 Object flush_result(&scope, thread->invokeMethod1(file, ID(flush)));
82 if (flush_result.isErrorException()) {
83 thread->clearPendingException();
84 }
85 return fd.isSmallInt() ? *fd : convertBoolToInt(*fd);
86}
87
88static void writeCStr(int fd, const char* str) {
89 File::write(fd, str, std::strlen(str));
90}
91
92static void handleFatalError(FaultHandler* handler) {
93 if (!fatal_error.enabled) return;
94
95 int saved_errno = errno;
96 disableFatalHandler(handler);
97
98 int fd = fatal_error.fd;
99 writeCStr(fd, "Fatal Python error: ");
100 writeCStr(fd, handler->msg);
101 writeCStr(fd, "\n\n");
102 // TODO(T66337218): Print tracebacks for all threads when there is more than
103 // one and`all_threads` is true.
104 fatal_error.runtime->printTraceback(Thread::current(), fd);
105
106 errno = saved_errno;
107 std::raise(handler->signum);
108}
109
110static void handleSigabrt(int signum) {
111 DCHECK(signum == SIGABRT, "expected SIGABRT (%d), got %d", SIGABRT, signum);
112 handleFatalError(&handler_sigabrt);
113}
114
115static void handleSigbus(int signum) {
116 DCHECK(signum == SIGBUS, "expected SIGBUS (%d), got %d", SIGBUS, signum);
117 handleFatalError(&handler_sigbus);
118}
119
120static void handleSigfpe(int signum) {
121 DCHECK(signum == SIGFPE, "expected SIGFPE (%d), got %d", SIGFPE, signum);
122 handleFatalError(&handler_sigfpe);
123}
124
125static void handleSigill(int signum) {
126 DCHECK(signum == SIGILL, "expected SIGILL (%d), got %d", SIGILL, signum);
127 handleFatalError(&handler_sigill);
128}
129
130static void handleSigsegv(int signum) {
131 DCHECK(signum == SIGSEGV, "expected SIGSEGV (%d), got %d", SIGSEGV, signum);
132 handleFatalError(&handler_sigsegv);
133}
134
135// Disable creation of core dump
136static void suppressCrashReport() {
137 struct rlimit rl;
138 if (::getrlimit(RLIMIT_CORE, &rl) == 0) {
139 rl.rlim_cur = 0;
140 ::setrlimit(RLIMIT_CORE, &rl);
141 }
142}
143
144RawObject FUNC(faulthandler, _read_null)(Thread*, Arguments) {
145 suppressCrashReport();
146 *static_cast<volatile word*>(nullptr);
147 return NoneType::object();
148}
149
150RawObject FUNC(faulthandler, _sigabrt)(Thread*, Arguments) {
151 suppressCrashReport();
152 std::abort();
153 return NoneType::object();
154}
155
156RawObject FUNC(faulthandler, _sigfpe)(Thread*, Arguments) {
157 suppressCrashReport();
158 std::raise(SIGFPE);
159 return NoneType::object();
160}
161
162RawObject FUNC(faulthandler, _sigsegv)(Thread*, Arguments) {
163 suppressCrashReport();
164 std::raise(SIGSEGV);
165 return NoneType::object();
166}
167
168RawObject FUNC(faulthandler, disable)(Thread*, Arguments) {
169 if (!fatal_error.enabled) {
170 return Bool::falseObj();
171 }
172
173 fatal_error.enabled = false;
174 disableFatalHandler(&handler_sigabrt);
175 disableFatalHandler(&handler_sigbus);
176 disableFatalHandler(&handler_sigill);
177 disableFatalHandler(&handler_sigfpe);
178 disableFatalHandler(&handler_sigsegv);
179 return Bool::trueObj();
180}
181
182RawObject FUNC(faulthandler, dump_traceback)(Thread* thread, Arguments args) {
183 HandleScope scope(thread);
184 Object file(&scope, args.get(0));
185 Object all_threads(&scope, args.get(1));
186
187 Runtime* runtime = thread->runtime();
188 if (!runtime->isInstanceOfInt(*all_threads)) {
189 return thread->raiseRequiresType(all_threads, ID(int));
190 }
191
192 Object fileno(&scope, getFileno(thread, file));
193 if (fileno.isError()) return *fileno;
194 SmallInt fd(&scope, *fileno);
195 // TODO(T66337218): Dump all threads when there is more than one and
196 // `all_threads` is True.
197 runtime->printTraceback(thread, fd.value());
198
199 return runtime->handlePendingSignals(thread);
200}
201
202static bool enableHandler(FaultHandler* handler, void (*handler_func)(int)) {
203 struct sigaction action;
204 action.sa_handler = handler_func;
205 sigemptyset(&action.sa_mask);
206 // For GC-safety, we execute all signal handlers on an alternate stack.
207 action.sa_flags = SA_NODEFER | SA_ONSTACK;
208
209 if (::sigaction(handler->signum, &action, &handler->previous_handler) != 0) {
210 return false;
211 }
212
213 handler->enabled = true;
214 return true;
215}
216
217RawObject FUNC(faulthandler, enable)(Thread* thread, Arguments args) {
218 HandleScope scope(thread);
219 Object file(&scope, args.get(0));
220 Object all_threads(&scope, args.get(1));
221 Runtime* runtime = thread->runtime();
222 if (!runtime->isInstanceOfInt(*all_threads)) {
223 return thread->raiseRequiresType(all_threads, ID(int));
224 }
225
226 Object fileno(&scope, getFileno(thread, file));
227 if (fileno.isError()) return *fileno;
228 fatal_error.fd = SmallInt::cast(*fileno).value();
229 fatal_error.all_threads = !intUnderlying(*all_threads).isZero();
230 fatal_error.runtime = runtime;
231 if (fatal_error.enabled) {
232 return NoneType::object();
233 }
234
235 fatal_error.enabled = true;
236
237 if (enableHandler(&handler_sigabrt, handleSigabrt) &&
238 enableHandler(&handler_sigbus, handleSigbus) &&
239 enableHandler(&handler_sigfpe, handleSigfpe) &&
240 enableHandler(&handler_sigill, handleSigill) &&
241 enableHandler(&handler_sigsegv, handleSigsegv)) {
242 return NoneType::object();
243 }
244
245 int saved_errno = errno;
246 const char* msg = std::strerror(saved_errno);
247 MutableTuple val(&scope, runtime->newMutableTuple(2));
248 val.atPut(0, SmallInt::fromWord(saved_errno));
249 val.atPut(1, runtime->newStrFromCStr(msg));
250 return thread->raise(LayoutId::kRuntimeError, val.becomeImmutable());
251}
252
253RawObject FUNC(faulthandler, is_enabled)(Thread*, Arguments) {
254 return Bool::fromBool(fatal_error.enabled);
255}
256
257} // namespace py