CCTLib
Calling-context and data-object attribution library for Intel Pin
exc_catch_and_resume.cpp
Go to the documentation of this file.
1 // Throw + catch + continue normally (no rethrow). After the catch block
2 // finishes, execution resumes right after the try/catch and the outer
3 // function returns cleanly. Then the OUTER caller does more work that
4 // deadspy must still attribute to the right CCT node.
5 //
6 // The purpose is to catch a hypothetical regression where cctlib
7 // forgets to fully restore its per-thread CCT anchor after an exception
8 // is caught locally - subsequent function calls would then be attributed
9 // to a stale ancestor and dead-write attribution would smear across
10 // unrelated contexts. Correct behavior: after `resume_after_catch`
11 // returns, the next call to `post_catch_worker` sees the same CCT
12 // parent as if no exception had happened.
13 //
14 // resume_try_marker (inside the try block of resume_after_catch) and
15 // resume_catch_marker (inside the catch block) both must appear as
16 // direct children of resume_after_catch, NOT under __cxa_throw.
17 #include <cstdint>
18 #include <cstdio>
19 #define ITERS 2000
20 static volatile uint64_t sink;
21 static uint64_t pre_marker[ITERS];
22 static uint64_t catch_marker[ITERS];
23 static uint64_t post_marker[ITERS];
24 
25 extern "C" __attribute__((noinline)) void resume_try_marker(int i) {
26  __asm__ __volatile__("" ::: "memory"); sink ^= (uint64_t)i;
27 }
28 extern "C" __attribute__((noinline)) void resume_catch_marker(int i) {
29  __asm__ __volatile__("" ::: "memory"); sink ^= (uint64_t)i << 8;
30 }
31 
32 static void may_throw(int i) {
33  pre_marker[i] = 0xAAAA;
34  if ((i & 1) == 0) throw i; // half the iters throw
35  pre_marker[i] |= 0x00BB0000; // odd iters skip the throw
36 }
37 
38 // Catch locally and return normally (no rethrow).
39 static void resume_after_catch(int i) {
40  try {
41  resume_try_marker(i);
42  may_throw(i);
43  } catch (int v) {
44  resume_catch_marker(i);
45  catch_marker[i] = (uint64_t)v ^ 0xCCCCCCCC;
46  }
47  // We land here for BOTH the throw and the non-throw path. cctlib's
48  // CCT anchor for this frame must be identical in both cases.
49 }
50 
51 // Simple downstream worker: cctlib should attribute these writes to
52 // the SAME CCT-node ancestor whether or not the try above threw.
53 static void post_catch_worker(int i) {
54  post_marker[i] = ((uint64_t)i * 0x1111ULL) ^ 0xF00DBABE;
55 }
56 
57 int main(int argc, char** argv) {
58  (void)argc; (void)argv;
59  for (int i = 0; i < ITERS; ++i) {
62  }
63  // Validate. Every iter should have:
64  // pre_marker[i] = 0xAAAA (even) or 0xAAAA | 0x00BB0000 (odd)
65  // catch_marker[i]= v ^ 0xCCCCCCCC for even (v==i); 0 for odd
66  // post_marker[i] = i*0x1111 ^ 0xF00DBABE for every i
67  int ok = 0;
68  for (int i = 0; i < ITERS; ++i) {
69  uint64_t expected_pre = (i & 1) ? (0xAAAAULL | 0x00BB0000ULL) : 0xAAAAULL;
70  uint64_t expected_catch = (i & 1) ? 0ULL : ((uint64_t)i ^ 0xCCCCCCCCULL);
71  uint64_t expected_post = ((uint64_t)i * 0x1111ULL) ^ 0xF00DBABEULL;
72  if (pre_marker[i] == expected_pre &&
73  catch_marker[i] == expected_catch &&
74  post_marker[i] == expected_post) ++ok;
75  sink ^= pre_marker[i] ^ catch_marker[i] ^ post_marker[i];
76  }
77  fprintf(stderr, "exc_catch_and_resume: ok=%d/%d sink=%llx\n",
78  ok, ITERS, (unsigned long long)sink);
79  return ok == ITERS ? 0 : 1;
80 }
static uint64_t pre_marker[ITERS]
static void post_catch_worker(int i)
#define ITERS
int main(int argc, char **argv)
__attribute__((noinline)) void resume_try_marker(int i)
static uint64_t post_marker[ITERS]
static void may_throw(int i)
static uint64_t catch_marker[ITERS]
static void resume_after_catch(int i)
static volatile uint64_t sink