Skip to content

Commit db84bca

Browse files
robert-smartbearRobert Bartoszewski
andauthored
Improved cxa_throw swapping (#1855)
* Improved cxa_throw swapping * Updated changelog --------- Co-authored-by: Robert Bartoszewski <robert.smartbear@gmail.com>
1 parent f906724 commit db84bca

File tree

8 files changed

+685
-19
lines changed

8 files changed

+685
-19
lines changed

Bugsnag.xcodeproj/project.pbxproj

Lines changed: 71 additions & 17 deletions
Large diffs are not rendered by default.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// KSPlatformSpecificDefines.h
3+
//
4+
// Copyright (c) 2019 YANDEX LLC. All rights reserved.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall remain in place
14+
// in this source code.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
//
24+
25+
#ifndef KSPlatformSpecificDefines_h
26+
#define KSPlatformSpecificDefines_h
27+
28+
#include <mach-o/loader.h>
29+
#include <mach-o/nlist.h>
30+
31+
#ifdef __LP64__
32+
typedef struct mach_header_64 mach_header_t;
33+
typedef struct segment_command_64 segment_command_t;
34+
typedef struct section_64 section_t;
35+
typedef struct nlist_64 nlist_t;
36+
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
37+
#else /* __LP64__ */
38+
typedef struct mach_header mach_header_t;
39+
typedef struct segment_command segment_command_t;
40+
typedef struct section section_t;
41+
typedef struct nlist nlist_t;
42+
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
43+
#endif /* __LP64__ */
44+
45+
#ifndef SEG_DATA_CONST
46+
#define SEG_DATA_CONST "__DATA_CONST"
47+
#endif /* SEG_DATA_CONST */
48+
49+
#endif /* KSPlatformSpecificDefines_h */

Bugsnag/KSCrash/Source/KSCrash/Recording/Sentry/BSG_KSCrashSentry_CPPException.mm

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "BSG_KSCrashSentry_Private.h"
3131
#include "BSG_KSCrashStringConversion.h"
3232
#include "BSG_KSMach.h"
33+
#include "../Tools/BSG_KSCxaThrowSwapper.h"
3334

3435
//#define BSG_KSLogger_LocalLevel TRACE
3536
#include "BSG_KSLogger.h"
@@ -84,10 +85,10 @@
8485
typedef void (*cxa_throw_type)(void *, std::type_info *, void (*)(void *));
8586

8687
extern "C" {
87-
void __cxa_throw(void *thrown_exception, std::type_info *tinfo,
88+
void BSG__cxa_throw_override(void *thrown_exception, std::type_info *tinfo,
8889
void (*dest)(void *)) __attribute__((weak));
8990

90-
void __cxa_throw(void *thrown_exception, std::type_info *tinfo,
91+
void BSG__cxa_throw_override(void *thrown_exception, std::type_info *tinfo,
9192
void (*dest)(void *)) {
9293
if (bsg_g_captureNextStackTrace) {
9394
bsg_g_stackTraceCount =
@@ -262,6 +263,7 @@ static void CPPExceptionTerminate(void) {
262263

263264
bsg_g_originalTerminateHandler = std::set_terminate(CPPExceptionTerminate);
264265
bsg_g_captureNextStackTrace = true;
266+
bsg_ksct_swap(BSG__cxa_throw_override);
265267
return true;
266268
}
267269

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
//
2+
// KSCxaThrowSwapper.cpp
3+
//
4+
// Copyright (c) 2019 YANDEX LLC. All rights reserved.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall remain in place
14+
// in this source code.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
//
24+
//
25+
// Inspired by facebook/fishhook
26+
// https://github.com/facebook/fishhook
27+
//
28+
// Copyright (c) 2013, Facebook, Inc.
29+
// All rights reserved.
30+
// Redistribution and use in source and binary forms, with or without
31+
// modification, are permitted provided that the following conditions are met:
32+
// * Redistributions of source code must retain the above copyright notice,
33+
// this list of conditions and the following disclaimer.
34+
// * Redistributions in binary form must reproduce the above copyright notice,
35+
// this list of conditions and the following disclaimer in the documentation
36+
// and/or other materials provided with the distribution.
37+
// * Neither the name Facebook nor the names of its contributors may be used to
38+
// endorse or promote products derived from this software without specific
39+
// prior written permission.
40+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
41+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
44+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
46+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
47+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
49+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50+
51+
#include "BSG_KSCxaThrowSwapper.h"
52+
53+
#include <dlfcn.h>
54+
#include <errno.h>
55+
#include <execinfo.h>
56+
#include <mach-o/dyld.h>
57+
#include <mach-o/nlist.h>
58+
#include <mach/mach.h>
59+
#include <stdio.h>
60+
#include <stdlib.h>
61+
#include <string.h>
62+
#include <sys/mman.h>
63+
#include <sys/types.h>
64+
65+
#include "BSG_KSLogger.h"
66+
#include "BSG_KSMach-O.h"
67+
#include "BSG_KSPlatformSpecificDefines.h"
68+
69+
typedef struct {
70+
uintptr_t image;
71+
uintptr_t function;
72+
} BSG_KSAddressPair;
73+
74+
static cxa_throw_type g_cxa_throw_handler = NULL;
75+
static const char *const g_cxa_throw_name = "__cxa_throw";
76+
77+
static BSG_KSAddressPair *g_cxa_originals = NULL;
78+
static size_t g_cxa_originals_capacity = 0;
79+
static size_t g_cxa_originals_count = 0;
80+
81+
static void addPair(BSG_KSAddressPair pair)
82+
{
83+
BSG_KSLOG_DEBUG("Adding address pair: image=%p, function=%p", (void *)pair.image, (void *)pair.function);
84+
85+
if (g_cxa_originals_count == g_cxa_originals_capacity) {
86+
g_cxa_originals_capacity *= 2;
87+
g_cxa_originals = (BSG_KSAddressPair *)realloc(g_cxa_originals, sizeof(BSG_KSAddressPair) * g_cxa_originals_capacity);
88+
if (g_cxa_originals == NULL) {
89+
BSG_KSLOG_ERROR("Failed to realloc memory for g_cxa_originals: %s", strerror(errno));
90+
return;
91+
}
92+
}
93+
memcpy(&g_cxa_originals[g_cxa_originals_count++], &pair, sizeof(BSG_KSAddressPair));
94+
}
95+
96+
static uintptr_t findAddress(void *address)
97+
{
98+
BSG_KSLOG_TRACE("Finding address for %p", address);
99+
100+
for (size_t i = 0; i < g_cxa_originals_count; i++) {
101+
if (g_cxa_originals[i].image == (uintptr_t)address) {
102+
return g_cxa_originals[i].function;
103+
}
104+
}
105+
BSG_KSLOG_WARN("Address %p not found", address);
106+
return (uintptr_t)NULL;
107+
}
108+
109+
static void __cxa_throw_decorator(void *thrown_exception, void *tinfo, void (*dest)(void *))
110+
{
111+
#define REQUIRED_FRAMES 2
112+
113+
BSG_KSLOG_TRACE("Decorating __cxa_throw");
114+
115+
g_cxa_throw_handler(thrown_exception, tinfo, dest);
116+
117+
void *backtraceArr[REQUIRED_FRAMES];
118+
int count = backtrace(backtraceArr, REQUIRED_FRAMES);
119+
120+
Dl_info info;
121+
if (count >= REQUIRED_FRAMES) {
122+
if (dladdr(backtraceArr[REQUIRED_FRAMES - 1], &info) != 0) {
123+
uintptr_t function = findAddress(info.dli_fbase);
124+
if (function != (uintptr_t)NULL) {
125+
BSG_KSLOG_TRACE("Calling original __cxa_throw function at %p", (void *)function);
126+
cxa_throw_type original = (cxa_throw_type)function;
127+
original(thrown_exception, tinfo, dest);
128+
}
129+
}
130+
}
131+
#undef REQUIRED_FRAMES
132+
}
133+
134+
static void perform_rebinding_with_section(const section_t *dataSection, intptr_t slide, nlist_t *symtab, char *strtab,
135+
uint32_t *indirect_symtab)
136+
{
137+
BSG_KSLOG_TRACE("Performing rebinding with section %s,%s", dataSection->segname, dataSection->sectname);
138+
139+
const bool isDataConst = strcmp(dataSection->segname, SEG_DATA_CONST) == 0;
140+
uint32_t *indirect_symbol_indices = indirect_symtab + dataSection->reserved1;
141+
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + dataSection->addr);
142+
vm_prot_t oldProtection = VM_PROT_READ;
143+
if (isDataConst) {
144+
oldProtection = bsg_ksmacho_getSectionProtection(indirect_symbol_bindings);
145+
if (mprotect(indirect_symbol_bindings, dataSection->size, PROT_READ | PROT_WRITE) != 0) {
146+
BSG_KSLOG_DEBUG("mprotect failed to set PROT_READ | PROT_WRITE for section %s,%s: %s", dataSection->segname,
147+
dataSection->sectname, strerror(errno));
148+
return;
149+
}
150+
}
151+
for (uint i = 0; i < dataSection->size / sizeof(void *); i++) {
152+
uint32_t symtab_index = indirect_symbol_indices[i];
153+
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
154+
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
155+
continue;
156+
}
157+
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
158+
char *symbol_name = strtab + strtab_offset;
159+
bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1];
160+
if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], g_cxa_throw_name) == 0) {
161+
Dl_info info;
162+
if (dladdr(dataSection, &info) != 0) {
163+
BSG_KSAddressPair pair = { (uintptr_t)info.dli_fbase, (uintptr_t)indirect_symbol_bindings[i] };
164+
addPair(pair);
165+
}
166+
indirect_symbol_bindings[i] = (void *)__cxa_throw_decorator;
167+
continue;
168+
}
169+
}
170+
if (isDataConst) {
171+
int protection = 0;
172+
if (oldProtection & VM_PROT_READ) {
173+
protection |= PROT_READ;
174+
}
175+
if (oldProtection & VM_PROT_WRITE) {
176+
protection |= PROT_WRITE;
177+
}
178+
if (oldProtection & VM_PROT_EXECUTE) {
179+
protection |= PROT_EXEC;
180+
}
181+
if (mprotect(indirect_symbol_bindings, dataSection->size, protection) != 0) {
182+
BSG_KSLOG_ERROR("mprotect failed to restore protection for section %s,%s: %s", dataSection->segname,
183+
dataSection->sectname, strerror(errno));
184+
}
185+
}
186+
}
187+
188+
static void process_segment(const struct mach_header *header, intptr_t slide, const char *segname, nlist_t *symtab,
189+
char *strtab, uint32_t *indirect_symtab)
190+
{
191+
BSG_KSLOG_DEBUG("Processing segment %s", segname);
192+
193+
const segment_command_t *segment = bsg_ksmacho_getSegmentByNameFromHeader((const mach_header_t *)header, segname);
194+
if (segment != NULL) {
195+
const section_t *lazy_sym_sect = bsg_ksmacho_getSectionByTypeFlagFromSegment(segment, S_LAZY_SYMBOL_POINTERS);
196+
const section_t *non_lazy_sym_sect =
197+
bsg_ksmacho_getSectionByTypeFlagFromSegment(segment, S_NON_LAZY_SYMBOL_POINTERS);
198+
199+
if (lazy_sym_sect != NULL) {
200+
perform_rebinding_with_section(lazy_sym_sect, slide, symtab, strtab, indirect_symtab);
201+
}
202+
if (non_lazy_sym_sect != NULL) {
203+
perform_rebinding_with_section(non_lazy_sym_sect, slide, symtab, strtab, indirect_symtab);
204+
}
205+
} else {
206+
BSG_KSLOG_WARN("Segment %s not found", segname);
207+
}
208+
}
209+
210+
static void rebind_symbols_for_image(const struct mach_header *header, intptr_t slide)
211+
{
212+
BSG_KSLOG_TRACE("Rebinding symbols for image with slide %p", (void *)slide);
213+
214+
Dl_info info;
215+
if (dladdr(header, &info) == 0) {
216+
BSG_KSLOG_WARN("dladdr failed");
217+
return;
218+
}
219+
BSG_KSLOG_TRACE("Image name: %s", info.dli_fname);
220+
if (slide == 0) {
221+
BSG_KSLOG_TRACE("Zero slide, can't do anything with it");
222+
return;
223+
}
224+
225+
const struct symtab_command *symtab_cmd =
226+
(const struct symtab_command *)bsg_ksmacho_getCommandByTypeFromHeader((const mach_header_t *)header, LC_SYMTAB);
227+
const struct dysymtab_command *dysymtab_cmd =
228+
(const struct dysymtab_command *)bsg_ksmacho_getCommandByTypeFromHeader((const mach_header_t *)header, LC_DYSYMTAB);
229+
const segment_command_t *linkedit_segment =
230+
bsg_ksmacho_getSegmentByNameFromHeader((const mach_header_t *)header, SEG_LINKEDIT);
231+
232+
if (symtab_cmd == NULL || dysymtab_cmd == NULL || linkedit_segment == NULL) {
233+
BSG_KSLOG_WARN("Required commands or segments not found");
234+
return;
235+
}
236+
237+
// Find base symbol/string table addresses
238+
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
239+
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
240+
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
241+
242+
// Get indirect symbol table (array of uint32_t indices into symbol table)
243+
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
244+
245+
process_segment(header, slide, SEG_DATA, symtab, strtab, indirect_symtab);
246+
process_segment(header, slide, SEG_DATA_CONST, symtab, strtab, indirect_symtab);
247+
}
248+
249+
int bsg_ksct_swap(const cxa_throw_type handler)
250+
{
251+
BSG_KSLOG_DEBUG("Swapping __cxa_throw handler");
252+
253+
if (g_cxa_originals == NULL) {
254+
g_cxa_originals_capacity = 25;
255+
g_cxa_originals = (BSG_KSAddressPair *)malloc(sizeof(BSG_KSAddressPair) * g_cxa_originals_capacity);
256+
if (g_cxa_originals == NULL) {
257+
BSG_KSLOG_ERROR("Failed to allocate memory for g_cxa_originals: %s", strerror(errno));
258+
return -1;
259+
}
260+
}
261+
g_cxa_originals_count = 0;
262+
263+
if (g_cxa_throw_handler == NULL) {
264+
g_cxa_throw_handler = handler;
265+
_dyld_register_func_for_add_image(rebind_symbols_for_image);
266+
} else {
267+
g_cxa_throw_handler = handler;
268+
uint32_t c = _dyld_image_count();
269+
for (uint32_t i = 0; i < c; i++) {
270+
rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
271+
}
272+
}
273+
return 0;
274+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// KSCxaThrowSwapper.h
3+
//
4+
// Copyright (c) 2019 YANDEX LLC. All rights reserved.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall remain in place
14+
// in this source code.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
//
24+
25+
#ifndef KSCxaThrowSwapper_h
26+
#define KSCxaThrowSwapper_h
27+
28+
#ifdef __cplusplus
29+
30+
#include <typeinfo>
31+
32+
extern "C" {
33+
34+
typedef void (*cxa_throw_type)(void *, std::type_info *, void (*)(void *));
35+
#else
36+
typedef void (*cxa_throw_type)(void *, void *, void (*)(void *));
37+
#endif
38+
39+
int bsg_ksct_swap(const cxa_throw_type handler);
40+
41+
#ifdef __cplusplus
42+
}
43+
#endif
44+
45+
#endif /* KSCxaThrowSwapper_h */

0 commit comments

Comments
 (0)