Skip to content

Commit c0d03b3

Browse files
janometa-codesync[bot]
authored andcommitted
Do not allow hack exceptions in setprofile callbacks
Summary: If a setprofile callback fails with a Hack exception, turn it into a fatal instead. Similar to what we do for many other handlers. It is not safe to throw Hack exceptions from some event hooks, such as EventHook::FunctionResumeAwait(), where they cause segfaults. Reviewed By: mdko Differential Revision: D107585135 fbshipit-source-id: 1b5de630f8f8e71b93ac5105e3d03a1b43442b05
1 parent b9faa9a commit c0d03b3

33 files changed

Lines changed: 266 additions & 281 deletions

hphp/runtime/vm/event-hook.cpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,14 @@ void runUserProfilerOnFunctionEnter(const ActRec* ar, bool isResume) {
339339
frameinfo
340340
);
341341

342-
// TODO(named_params) thread named arg names to profiling
343-
g_context->invokeFunc(func, params, nullptr /* namedArgNames */, ctx.this_,
344-
ctx.cls, RuntimeCoeffects::defaults(), ctx.dynamic);
342+
try {
343+
// TODO(named_params) thread named arg names to profiling
344+
g_context->invokeFunc(func, params, nullptr /* namedArgNames */, ctx.this_,
345+
ctx.cls, RuntimeCoeffects::defaults(), ctx.dynamic);
346+
} catch (Object& ex) {
347+
raise_error("Uncaught exception escaping setprofile callback: %s",
348+
throwable_to_string(ex.get()).data());
349+
}
345350
}
346351

347352
void runUserProfilerOnFunctionExit(const ActRec* ar, const TypedValue* retval,
@@ -374,10 +379,15 @@ void runUserProfilerOnFunctionExit(const ActRec* ar, const TypedValue* retval,
374379
frameinfo
375380
);
376381

377-
// TODO(named_params) add user profiler support for calls with named args.
378-
const ArrayData* namedArgNames = nullptr;
379-
g_context->invokeFunc(func, params, namedArgNames, ctx.this_, ctx.cls,
380-
RuntimeCoeffects::defaults(), ctx.dynamic);
382+
try {
383+
// TODO(named_params) add user profiler support for calls with named args.
384+
const ArrayData* namedArgNames = nullptr;
385+
g_context->invokeFunc(func, params, namedArgNames, ctx.this_, ctx.cls,
386+
RuntimeCoeffects::defaults(), ctx.dynamic);
387+
} catch (Object& ex) {
388+
raise_error("Uncaught exception escaping setprofile callback: %s",
389+
throwable_to_string(ex.get()).data());
390+
}
381391
}
382392

383393
static Variant call_intercept_handler(

hphp/test/quick/constructor_throw.php.expect

Lines changed: 0 additions & 1 deletion
This file was deleted.

hphp/test/quick/constructor_throw.php renamed to hphp/test/quick/profile/constructor_throw.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ function handler($kind, $name) :mixed{
1515
try {
1616
new X;
1717
} catch (Exception $e) {
18-
echo "ok\n";
18+
echo "not ok\n";
1919
}
2020
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
Fatal error: Uncaught exception escaping setprofile callback: exception 'Exception' with message '' in %s/constructor_throw.php:10
3+
Stack trace:
4+
#0 %s/constructor_throw.php(6): handler()
5+
#1 %s/constructor_throw.php(16): X->__construct()
6+
#2 (): test()
7+
#3 %s/constructor_throw.php on line 6

hphp/test/quick/profile/setprofile-this.php

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public function method() :mixed{
2020

2121
abstract final class ProfilerStatics {
2222
public static $indent = 2;
23-
public static $threw = false;
2423
}
2524

2625
function profiler($event, $name, $info) :mixed{
@@ -30,30 +29,11 @@ function profiler($event, $name, $info) :mixed{
3029
printf("\n%s%s %s: %s\n", str_repeat(' ', ProfilerStatics::$indent), $event,
3130
$name, serialize($info));
3231
if ($event == 'enter') ++ProfilerStatics::$indent;
33-
if ($event == 'exit' &&
34-
((!ProfilerStatics::$threw && strncmp('C::', $name, 3) == 0) ||
35-
$name === 'C::method')) {
36-
ProfilerStatics::$threw = true;
37-
throw new Exception($name);
38-
}
3932
}
4033

41-
function main() :mixed{
42-
try {
43-
new C();
44-
} catch (Exception $e) {
45-
echo "\nCaught ".$e->getMessage()."\n";
46-
}
47-
48-
try {
49-
(new C())->method();
50-
} catch (Exception $e) {
51-
echo "\nCaught ".$e->getMessage()."\n";
52-
}
53-
}
5434
<<__EntryPoint>>
55-
function entrypoint_setprofilethis(): void {
56-
35+
function setprofile_this(): void {
5736
fb_setprofile(profiler<>);
58-
main();
37+
new C();
38+
(new C())->method();
5939
}
Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,28 @@
1-
exit fb_setprofile: D:1:{s:6:"return";N;}
21

3-
enter main: D:1:{s:4:"args";v:0:{}}
2+
exit fb_setprofile: D:1:{s:6:"return";N;}
43

5-
enter C::__construct: D:1:{s:4:"args";v:0:{}}
4+
enter C::__construct: D:1:{s:4:"args";v:0:{}}
65

76
C constructing
87

9-
exit C::__construct: D:1:{s:6:"return";N;}
8+
exit C::__construct: D:1:{s:6:"return";N;}
109

11-
enter Exception::getMessage: D:1:{s:4:"args";v:0:{}}
12-
13-
exit Exception::getMessage: D:1:{s:6:"return";s:14:"C::__construct";}
14-
15-
Caught C::__construct
16-
17-
enter C::__construct: D:1:{s:4:"args";v:0:{}}
10+
enter C::__construct: D:1:{s:4:"args";v:0:{}}
1811

1912
C constructing
2013

21-
exit C::__construct: D:1:{s:6:"return";N;}
14+
exit C::__construct: D:1:{s:6:"return";N;}
2215

23-
enter C::method: D:1:{s:4:"args";v:0:{}}
16+
enter C::method: D:1:{s:4:"args";v:0:{}}
2417

2518
C method
2619

27-
enter A::__construct: D:1:{s:4:"args";v:0:{}}
20+
enter A::__construct: D:1:{s:4:"args";v:0:{}}
2821

2922
A constructing
3023

31-
exit A::__construct: D:1:{s:6:"return";N;}
32-
33-
exit C::method: D:1:{s:6:"return";O:1:"A":0:{}}
34-
35-
enter Exception::getMessage: D:1:{s:4:"args";v:0:{}}
36-
37-
exit Exception::getMessage: D:1:{s:6:"return";s:9:"C::method";}
38-
39-
Caught C::method
24+
exit A::__construct: D:1:{s:6:"return";N;}
4025

41-
exit main: D:1:{s:6:"return";N;}
26+
exit C::method: D:1:{s:6:"return";O:1:"A":0:{}}
4227

43-
exit entrypoint_setprofilethis: D:1:{s:6:"return";N;}
28+
exit setprofile_this: D:1:{s:6:"return";N;}

hphp/test/quick/profile/setprofile_throw.php

Lines changed: 2 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -14,86 +14,11 @@ function foo($f) :mixed{
1414
$f();
1515
}
1616

17-
function signal_thrower() :mixed{
18-
echo "signal throwing\n";
19-
throw new Exception("Sig exception");
20-
}
21-
22-
function func_to_enter() :mixed{}
23-
24-
class DtorObj {
25-
public function __construct($x) { $this->x = $x; echo "__ctor $x\n"; }
26-
}
27-
28-
// During function exit
29-
function func_entry() :mixed{
30-
$x1 = new DtorObj(1);
31-
$x2 = new DtorObj(2);
32-
$x3 = new DtorObj(3);
33-
$x4 = new DtorObj(4);
34-
$x5 = new DtorObj(5);
35-
36-
$x6 = new DtorObj(6);
37-
$x7 = new DtorObj(7);
38-
$x8 = new DtorObj(8);
39-
$x9 = new DtorObj(9);
40-
$xa = new DtorObj(0xa);
41-
42-
$xb = new DtorObj(0xb);
43-
$xc = new DtorObj(0xc);
44-
$xd = new DtorObj(0xd);
45-
$xe = new DtorObj(0xe);
46-
$xf = new DtorObj(0xf);
47-
48-
posix_kill(posix_getpid(), SIGUSR1);
49-
50-
// Surprise flags are checked on function entry.
51-
func_to_enter();
52-
}
53-
54-
// During backward branches
55-
function func_backward() :mixed{
56-
$x1 = new DtorObj(1);
57-
$x2 = new DtorObj(2);
58-
$x3 = new DtorObj(3);
59-
$x4 = new DtorObj(4);
60-
$x5 = new DtorObj(5);
61-
62-
$x6 = new DtorObj(6);
63-
$x7 = new DtorObj(7);
64-
$x8 = new DtorObj(8);
65-
$x9 = new DtorObj(9);
66-
$xa = new DtorObj(0xa);
67-
68-
$xb = new DtorObj(0xb);
69-
$xc = new DtorObj(0xc);
70-
$xd = new DtorObj(0xd);
71-
$xe = new DtorObj(0xe);
72-
$xf = new DtorObj(0xf);
73-
74-
posix_kill(posix_getpid(), SIGUSR1);
75-
76-
// Surprise flags are checked on backward branch.
77-
for ($i = 0; $i < 2; ++$i) {}
78-
}
79-
80-
function main() :mixed{
17+
<<__EntryPoint>>
18+
function setprofile_throw(): void {
8119
// Test throwing on function entry
8220
fb_setprofile('throwing_profiler');
8321
try { foo('bar'); } catch (Exception $x) { echo "Caught\n"; }
8422
try { foo('baz'); } catch (Exception $x) { echo "Caught\n"; }
8523
fb_setprofile(null);
86-
87-
// Test throwing exceptions from surprise flags things (OOM, signals,
88-
// and req timeout all work this way).
89-
90-
pcntl_signal(SIGUSR1, 'signal_thrower');
91-
92-
try { func_entry(); } catch (Exception $x) { echo "caught\n"; }
93-
try { func_backward(); } catch (Exception $x) { echo "caught\n"; }
94-
}
95-
<<__EntryPoint>>
96-
function entrypoint_setprofile_throw(): void {
97-
98-
main();
9924
}
Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,8 @@
1-
Caught
2-
Caught
3-
__ctor 1
4-
__ctor 2
5-
__ctor 3
6-
__ctor 4
7-
__ctor 5
8-
__ctor 6
9-
__ctor 7
10-
__ctor 8
11-
__ctor 9
12-
__ctor 10
13-
__ctor 11
14-
__ctor 12
15-
__ctor 13
16-
__ctor 14
17-
__ctor 15
18-
signal throwing
191

20-
Warning: signal handler 'signal_thrower' threw exception 'Exception' with message 'Sig exception' in %s/setprofile_throw.php:19
2+
Fatal error: Uncaught exception escaping setprofile callback: exception 'Exception' with message 'yeah' in %s/setprofile_throw.php:6
213
Stack trace:
22-
#0 (): signal_thrower()
23-
#1 %s/setprofile_throw.php(48): posix_kill()
24-
#2 %s/setprofile_throw.php(92): func_entry()
25-
#3 %s/setprofile_throw.php(98): main()
26-
#4 (): entrypoint_setprofile_throw()
27-
#5 {main} in %s/setprofile_throw.php on line 48
28-
__ctor 1
29-
__ctor 2
30-
__ctor 3
31-
__ctor 4
32-
__ctor 5
33-
__ctor 6
34-
__ctor 7
35-
__ctor 8
36-
__ctor 9
37-
__ctor 10
38-
__ctor 11
39-
__ctor 12
40-
__ctor 13
41-
__ctor 14
42-
__ctor 15
43-
signal throwing
44-
45-
Warning: signal handler 'signal_thrower' threw exception 'Exception' with message 'Sig exception' in %s/setprofile_throw.php:19
46-
Stack trace:
47-
#0 (): signal_thrower()
48-
#1 %s/setprofile_throw.php(74): posix_kill()
49-
#2 %s/setprofile_throw.php(93): func_backward()
50-
#3 %s/setprofile_throw.php(98): main()
51-
#4 (): entrypoint_setprofile_throw()
52-
#5 {main} in %s/setprofile_throw.php on line 74
4+
#0 %s/setprofile_throw.php(10): throwing_profiler()
5+
#1 %s/setprofile_throw.php(14): bar()
6+
#2 %s/setprofile_throw.php(21): foo()
7+
#3 (): setprofile_throw()
8+
#4 {main} in %s/setprofile_throw.php on line 10
File renamed without changes.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
hi exit fb_setprofile
2+
hi enter foo
3+
yep
4+
5+
Fatal error: Uncaught exception escaping setprofile callback: exception 'Exception' with message 'yo' in %s/surprise_throw.php:7
6+
Stack trace:
7+
#0 %s/surprise_throw.php(16): asd()
8+
#1 %s/surprise_throw.php(23): foo()
9+
#2 (): entrypoint_surprise_throw()
10+
#3 {main} in %s/surprise_throw.php on line 16

0 commit comments

Comments
 (0)