contrast-agent 3.8.5 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/cs__assess_array/cs__assess_array.c +1 -1
- data/ext/cs__assess_module/cs__assess_module.c +0 -1
- data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
- data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
- data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
- data/ext/cs__common/cs__common.c +6 -6
- data/ext/cs__common/cs__common.h +3 -1
- data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
- data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
- data/funchook/autom4te.cache/requests +48 -48
- data/funchook/config.log +2 -2
- data/lib/contrast/agent.rb +15 -5
- data/lib/contrast/agent/assess.rb +0 -1
- data/lib/contrast/agent/assess/contrast_event.rb +9 -8
- data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
- data/lib/contrast/agent/assess/policy/policy.rb +0 -14
- data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
- data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
- data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
- data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
- data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
- data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
- data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
- data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
- data/lib/contrast/agent/assess/properties.rb +29 -0
- data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
- data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
- data/lib/contrast/agent/class_reopener.rb +98 -55
- data/lib/contrast/agent/feature_state.rb +1 -1
- data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
- data/lib/contrast/agent/logger_manager.rb +2 -2
- data/lib/contrast/agent/middleware.rb +1 -3
- data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
- data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
- data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
- data/lib/contrast/agent/patching/policy/patch.rb +54 -23
- data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
- data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
- data/lib/contrast/agent/patching/policy/policy.rb +4 -0
- data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
- data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
- data/lib/contrast/agent/protect/policy/policy.rb +6 -6
- data/lib/contrast/agent/protect/rule/base.rb +1 -1
- data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
- data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
- data/lib/contrast/agent/railtie.rb +11 -5
- data/lib/contrast/agent/request.rb +1 -19
- data/lib/contrast/agent/request_context.rb +1 -1
- data/lib/contrast/agent/rewriter.rb +4 -3
- data/lib/contrast/agent/scope.rb +116 -19
- data/lib/contrast/agent/service_heartbeat.rb +5 -2
- data/lib/contrast/agent/settings_state.rb +12 -8
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/api.rb +1 -0
- data/lib/contrast/api/speedracer.rb +2 -2
- data/lib/contrast/components/agent.rb +26 -7
- data/lib/contrast/components/app_context.rb +8 -45
- data/lib/contrast/components/contrast_service.rb +3 -4
- data/lib/contrast/components/interface.rb +1 -1
- data/lib/contrast/components/scope.rb +56 -26
- data/lib/contrast/config/ruby_configuration.rb +8 -3
- data/lib/contrast/delegators.rb +9 -0
- data/lib/contrast/delegators/application_update.rb +32 -0
- data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
- data/lib/contrast/extensions/framework/rack/request.rb +24 -0
- data/lib/contrast/extensions/framework/rack/response.rb +23 -0
- data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
- data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
- data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
- data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
- data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
- data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
- data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
- data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
- data/lib/contrast/extensions/ruby_core/module.rb +17 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
- data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
- data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
- data/lib/contrast/framework/base_support.rb +63 -0
- data/lib/contrast/framework/manager.rb +109 -0
- data/lib/contrast/framework/platform_version.rb +21 -0
- data/lib/contrast/framework/rails_support.rb +88 -0
- data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
- data/lib/contrast/framework/sinatra_support.rb +94 -0
- data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
- data/lib/contrast/utils/assess/tracking_util.rb +2 -4
- data/lib/contrast/utils/class_util.rb +92 -37
- data/lib/contrast/utils/duck_utils.rb +59 -39
- data/lib/contrast/utils/environment_util.rb +5 -75
- data/lib/contrast/utils/freeze_util.rb +3 -7
- data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
- data/lib/contrast/utils/job_servers_running.rb +39 -0
- data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
- data/lib/contrast/utils/service_response_util.rb +0 -6
- data/lib/contrast/utils/sinatra_helper.rb +6 -0
- data/lib/contrast/utils/stack_trace_utils.rb +1 -1
- data/resources/assess/policy.json +74 -23
- data/resources/inventory/policy.json +1 -1
- data/resources/protect/policy.json +11 -9
- data/resources/rubocops/object/frozen_cop.rb +1 -1
- data/ruby-agent.gemspec +2 -0
- data/service_executables/VERSION +1 -1
- data/service_executables/linux/contrast-service +0 -0
- data/service_executables/mac/contrast-service +0 -0
- metadata +94 -57
- data/ext/cs__scope/cs__scope.c +0 -96
- data/ext/cs__scope/cs__scope.h +0 -33
- data/lib/contrast/agent/assess/class_reverter.rb +0 -82
- data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
- data/lib/contrast/core_extensions/module.rb +0 -42
- data/lib/contrast/core_extensions/object.rb +0 -27
- data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
- data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
- data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
- data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
- data/lib/contrast/rails_extensions/buffer.rb +0 -30
- data/lib/contrast/rails_extensions/rack.rb +0 -45
- data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
- data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
- data/lib/contrast/utils/operating_environment.rb +0 -38
- data/lib/contrast/utils/path_util.rb +0 -151
- data/lib/contrast/utils/scope_util.rb +0 -99
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cc76e7cc9a47ace8f43df83fca283ee52dc8f4eb48bee2bca660981616c43124
|
|
4
|
+
data.tar.gz: c07ab0d747605fa69730f4409543ccd9ce7be5624f5d959ec27bd1dd227551db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 609dd7c247d30d0ec1a7567736d3d5377a422c1ba7e52ae9c52409a192e2f90f5054ef4f34b35e39d78bf5d05197449e791f2af5dc333b4d164fa9ccbfc015c1
|
|
7
|
+
data.tar.gz: c5336c941c6aece474c548549587e93fe61561ce8d4ef0576d5aefe875b0895a9f8e5c26afe2b41912f638eab9976c9357e268eee84c7cd6d4145aa507deaa86
|
|
@@ -30,7 +30,7 @@ static VALUE contrast_assess_array_join(const int argc, const VALUE *argv,
|
|
|
30
30
|
|
|
31
31
|
void Init_cs__assess_array(void) {
|
|
32
32
|
rb_sym_assess_array_join = rb_intern("cs__patched_join");
|
|
33
|
-
rb_sym_assess_track_array_join = rb_intern("
|
|
33
|
+
rb_sym_assess_track_array_join = rb_intern("cs__track_join");
|
|
34
34
|
|
|
35
35
|
VALUE array_class = rb_define_class("Array", rb_cObject);
|
|
36
36
|
contrast_alias_method(array_class, "cs__patched_join", "join");
|
|
@@ -61,7 +61,6 @@ contrast_assess_module_module_eval(const int argc, const VALUE *argv,
|
|
|
61
61
|
void Init_cs__assess_module(void) {
|
|
62
62
|
rb_sym_assess_patch_eval = rb_intern("patch_assess_on_eval");
|
|
63
63
|
|
|
64
|
-
VALUE assess_policy = rb_define_module_under(assess, "Policy");
|
|
65
64
|
assess_patcher = rb_define_module_under(assess_policy, "Patcher");
|
|
66
65
|
|
|
67
66
|
trigger_check_method = rb_intern("eval_trigger_check");
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* Copyright (c) 2020 Contrast Security, Inc. See
|
|
2
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a for more details. */
|
|
3
|
+
|
|
4
|
+
#include "cs__assess_yield_track.h"
|
|
5
|
+
#include "../cs__common/cs__common.h"
|
|
6
|
+
#include <funchook.h>
|
|
7
|
+
#include <ruby.h>
|
|
8
|
+
|
|
9
|
+
static VALUE rb_yield_hook(VALUE val, const VALUE self) {
|
|
10
|
+
VALUE method = rb_funcall(rb_mKernel, rb_sym_method, 0);
|
|
11
|
+
|
|
12
|
+
if(method == split_method && RB_TYPE_P(val, T_STRING)) {
|
|
13
|
+
rb_funcall(split_class, propagate_yield, 1, val);
|
|
14
|
+
}
|
|
15
|
+
VALUE result = rb_yield_original(val);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static int install_yield_hooks() {
|
|
20
|
+
funchook_t *funchook = funchook_create();
|
|
21
|
+
rb_yield_original = rb_yield;
|
|
22
|
+
funchook_prepare(funchook, (void **)&rb_yield_original,
|
|
23
|
+
rb_yield_hook);
|
|
24
|
+
funchook_install(funchook, 0);
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void Init_cs__assess_yield_track(void) {
|
|
29
|
+
VALUE base = rb_define_class_under(assess_propagator, "Base", rb_cObject);
|
|
30
|
+
split_class = rb_define_class_under(assess_propagator, "Split", base);
|
|
31
|
+
propagate_yield = rb_intern("propagate_yield");
|
|
32
|
+
split_method = ID2SYM(rb_intern("split"));
|
|
33
|
+
install_yield_hooks();
|
|
34
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#include <funchook.h>
|
|
2
|
+
#include <ruby.h>
|
|
3
|
+
|
|
4
|
+
static VALUE split_class;
|
|
5
|
+
static VALUE propagate_yield, split_method;
|
|
6
|
+
|
|
7
|
+
static VALUE *(*rb_yield_original)(VALUE val);
|
|
8
|
+
static VALUE rb_yield_hook(VALUE val, const VALUE self);
|
|
9
|
+
|
|
10
|
+
static int install_yield_hooks();
|
|
11
|
+
|
|
12
|
+
void Init_cs__assess_yield_track(void);
|
|
File without changes
|
data/ext/cs__common/cs__common.c
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
/* Globals */
|
|
8
8
|
/* These are defined w/ `extern` in the header */
|
|
9
|
-
VALUE contrast, agent, patching, policy, assess
|
|
9
|
+
VALUE contrast, agent, patching, policy, assess;
|
|
10
|
+
VALUE core_extensions, core_assess;
|
|
11
|
+
VALUE assess_policy, assess_propagator;
|
|
10
12
|
|
|
11
13
|
VALUE rb_sym_enter_scope;
|
|
12
14
|
VALUE rb_sym_exit_scope;
|
|
@@ -50,11 +52,9 @@ void Init_cs__common(void) {
|
|
|
50
52
|
policy = rb_define_module_under(patching, "Policy");
|
|
51
53
|
patcher = rb_define_module_under(policy, "Patch");
|
|
52
54
|
|
|
55
|
+
assess_policy = rb_define_module_under(assess, "Policy");
|
|
56
|
+
assess_propagator = rb_define_module_under(assess_policy, "Propagator");
|
|
57
|
+
|
|
53
58
|
core_extensions = rb_define_module_under(contrast, "CoreExtensions");
|
|
54
59
|
core_assess = rb_define_module_under(core_extensions, "Assess");
|
|
55
|
-
|
|
56
|
-
/* Ensure ScopeUtil is set on the Contrast patcher */
|
|
57
|
-
VALUE utils = rb_define_module_under(contrast, "Utils");
|
|
58
|
-
VALUE scopeUtil = rb_define_module_under(utils, "ScopeUtil");
|
|
59
|
-
rb_extend_object(patcher, scopeUtil);
|
|
60
60
|
}
|
data/ext/cs__common/cs__common.h
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
static VALUE cs__send_method;
|
|
7
7
|
static VALUE cs__alias_method_sym;
|
|
8
8
|
|
|
9
|
-
extern VALUE contrast, agent, patching, policy, assess
|
|
9
|
+
extern VALUE contrast, agent, patching, policy, assess;
|
|
10
|
+
extern VALUE core_extensions, core_assess;
|
|
11
|
+
extern VALUE assess_policy, assess_propagator;
|
|
10
12
|
|
|
11
13
|
extern VALUE rb_sym_enter_scope;
|
|
12
14
|
extern VALUE rb_sym_exit_scope;
|
|
@@ -28,11 +28,12 @@ VALUE contrast_patch_call_original(const VALUE *args) {
|
|
|
28
28
|
int argc;
|
|
29
29
|
VALUE method, method_id, object;
|
|
30
30
|
VALUE *params;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
argc = NUM2INT(args[0]);
|
|
32
|
+
params = (VALUE *)args[1];
|
|
33
|
+
object = args[2];
|
|
34
|
+
method = args[3];
|
|
33
35
|
method_id = SYM2ID(method);
|
|
34
|
-
|
|
35
|
-
params = (VALUE *)args[3];
|
|
36
|
+
|
|
36
37
|
|
|
37
38
|
/* It looks like we can find the last Ruby block given so long as we don't
|
|
38
39
|
* change Ruby method scope (always call this function from C, not Ruby),
|
|
@@ -94,9 +95,6 @@ VALUE contrast_patch_call_rescue(const VALUE *args) {
|
|
|
94
95
|
|
|
95
96
|
contrast_call_post_patch(method_policy, preshift, object, Qnil, argc, argv);
|
|
96
97
|
|
|
97
|
-
/* exit scope */
|
|
98
|
-
rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
|
|
99
|
-
|
|
100
98
|
/* reraise the exception that got us here */
|
|
101
99
|
rb_exc_raise(exception);
|
|
102
100
|
|
|
@@ -104,19 +102,119 @@ VALUE contrast_patch_call_rescue(const VALUE *args) {
|
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
VALUE contrast_call_super(const VALUE *args) {
|
|
107
|
-
int argc
|
|
108
|
-
VALUE *argv
|
|
105
|
+
int argc;
|
|
106
|
+
VALUE *argv;
|
|
107
|
+
argc = NUM2INT(args[0]);
|
|
108
|
+
argv = (VALUE *)args[1];
|
|
109
109
|
|
|
110
110
|
return rb_call_super(argc, argv);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
VALUE contrast_run_patches(const VALUE *wrapped_args) {
|
|
114
|
+
VALUE impl, method, method_policy, object, original_args, original_ret, preshift, transformed_ret;
|
|
115
|
+
int argc;
|
|
116
|
+
VALUE *argv;
|
|
117
|
+
VALUE rescue_args[6];
|
|
118
|
+
|
|
119
|
+
impl = wrapped_args[0];
|
|
120
|
+
original_args = wrapped_args[1];
|
|
121
|
+
method = wrapped_args[2];
|
|
122
|
+
method_policy = wrapped_args[3];
|
|
123
|
+
object = wrapped_args[4];
|
|
124
|
+
argc = NUM2INT(wrapped_args[5]);
|
|
125
|
+
argv = (VALUE *)wrapped_args[6];
|
|
126
|
+
|
|
127
|
+
rescue_args[0] = object;
|
|
128
|
+
rescue_args[1] = method;
|
|
129
|
+
rescue_args[2] = INT2NUM(argc);
|
|
130
|
+
rescue_args[3] = (VALUE)argv;
|
|
131
|
+
rescue_args[4] = method_policy;
|
|
132
|
+
|
|
133
|
+
/* Tracking, triggering, and propagation here. */
|
|
134
|
+
contrast_call_pre_patch(method_policy, method, object, argc, argv, Qnil);
|
|
135
|
+
|
|
136
|
+
/* Capture pre-call state */
|
|
137
|
+
preshift = build_preshift(method_policy, object, argc, argv);
|
|
138
|
+
rescue_args[5] = preshift;
|
|
139
|
+
|
|
140
|
+
/* We wrap a call to the original method with a rescue block, and we use
|
|
141
|
+
* rb_rescue2 to capture all Exception-inheriting exceptions (and if your
|
|
142
|
+
* software is well-behaved, all exceptions should inherit from Exception.)
|
|
143
|
+
*
|
|
144
|
+
* The rescue block is responsible for doing Contrast post-call analysis
|
|
145
|
+
* in the event the original method has thrown an exception.
|
|
146
|
+
*
|
|
147
|
+
* EDGE CASES:
|
|
148
|
+
* Given how extensively we patch and instrument, this code is
|
|
149
|
+
* prone to some esoteric edge cases that are not well-documented or
|
|
150
|
+
* easy to research.
|
|
151
|
+
*
|
|
152
|
+
* There is an esoteric edge case in core Ruby, upon Thread#kill, where
|
|
153
|
+
* it raises Fixnum 8 (Qnil==8). This is an intentional choice on the
|
|
154
|
+
* part of the core Ruby devs, as blindly rescuing Thread#kill would be
|
|
155
|
+
* disastrous.
|
|
156
|
+
* A consequence of this is that Thread#kill will leak scope, if you
|
|
157
|
+
* happen to ever instrument it.
|
|
158
|
+
*
|
|
159
|
+
* If you are within a catch block, and the original function results
|
|
160
|
+
* in a throw, you will leak scope. We handle this by not instrumenting
|
|
161
|
+
* methods that do that. (Tracked in RUBY-552.)
|
|
162
|
+
*
|
|
163
|
+
* If you're thinking of cleaning this up by using rb_protect,
|
|
164
|
+
* you will catch ALL exceptions, as well as ANYTHING
|
|
165
|
+
* else that unwinds the stack. This includes fiber context switches
|
|
166
|
+
* (which are used to implement Enumerator#next) and catch/throw blocks.
|
|
167
|
+
* I spent a week debugging that so you don't have to. -ajm
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
switch (impl) {
|
|
171
|
+
case IMPL_ALIAS_INSTANCE:
|
|
172
|
+
case IMPL_ALIAS_SINGLETON:
|
|
173
|
+
original_ret = rb_rescue2(
|
|
174
|
+
contrast_patch_call_original, original_args,
|
|
175
|
+
contrast_patch_call_rescue, (VALUE)rescue_args, rb_eException, 0);
|
|
176
|
+
break;
|
|
177
|
+
case IMPL_PREPEND:
|
|
178
|
+
original_ret = rb_rescue2(contrast_call_super, original_args,
|
|
179
|
+
contrast_patch_call_rescue,
|
|
180
|
+
(VALUE)rescue_args, rb_eException, 0);
|
|
181
|
+
break;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/* If you're here, the original method did not throw an exception
|
|
185
|
+
* (or unwind the stack otherwise).
|
|
186
|
+
* If the original method threw an exception, contrast_patch_call_rescue
|
|
187
|
+
* re-raises the original exception, which unwinds the stack back to the
|
|
188
|
+
* call site. This means the rest of this function is not executed.
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
/* Invoke Contrast post-call patching.
|
|
192
|
+
* Post-call patching may transform the return value,
|
|
193
|
+
* hence the assignment.
|
|
194
|
+
*/
|
|
195
|
+
transformed_ret = contrast_call_post_patch(method_policy, preshift, object,
|
|
196
|
+
original_ret, argc, argv);
|
|
197
|
+
|
|
198
|
+
/* Special case for tracking frozen sources */
|
|
199
|
+
if (transformed_ret != Qnil) {
|
|
200
|
+
return transformed_ret;
|
|
201
|
+
} else {
|
|
202
|
+
return original_ret;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
VALUE contrast_ensure_function(const VALUE method_policy) {
|
|
207
|
+
/* exit scope */
|
|
208
|
+
rb_funcall(contrast_patcher(), rb_sym_exit_method_scope, 1, method_policy);
|
|
209
|
+
rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
|
|
210
|
+
|
|
211
|
+
return Qnil;
|
|
212
|
+
}
|
|
213
|
+
|
|
113
214
|
VALUE contrast_patch_dispatch(const int argc, const VALUE *argv,
|
|
114
215
|
const patch_impl impl, const VALUE object) {
|
|
115
|
-
VALUE cs__method, known, method,
|
|
116
|
-
method_policy, preshift;
|
|
216
|
+
VALUE cs__method, known, method, method_policy;
|
|
117
217
|
VALUE original_args[4];
|
|
118
|
-
VALUE rescue_args[7];
|
|
119
|
-
|
|
120
218
|
int do_contrast, nested_scope;
|
|
121
219
|
|
|
122
220
|
/* Do Contrast analysis, unless our subsequent checks tell us no. */
|
|
@@ -158,18 +256,6 @@ VALUE contrast_patch_dispatch(const int argc, const VALUE *argv,
|
|
|
158
256
|
method_policy = Qnil;
|
|
159
257
|
}
|
|
160
258
|
|
|
161
|
-
if (impl == IMPL_ALIAS_INSTANCE || impl == IMPL_ALIAS_SINGLETON) {
|
|
162
|
-
/* Alias patching moves the original method to "cs__#{method}" */
|
|
163
|
-
cs__method = rb_funcall(known, rb_sym_brackets, 1, INT2NUM(1));
|
|
164
|
-
|
|
165
|
-
/* We may not have built the alias yet */
|
|
166
|
-
if (!RTEST(cs__method)) {
|
|
167
|
-
cs__method =
|
|
168
|
-
rb_funcall(contrast_patcher(), rb_sym_build_method_name, 2,
|
|
169
|
-
object, method);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
259
|
/* Check conditions for not doing Contrast analysis */
|
|
174
260
|
if (nested_scope) {
|
|
175
261
|
/* if we were in scope */
|
|
@@ -190,112 +276,47 @@ VALUE contrast_patch_dispatch(const int argc, const VALUE *argv,
|
|
|
190
276
|
do_contrast = 0;
|
|
191
277
|
}
|
|
192
278
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
279
|
+
original_args[0] = INT2NUM(argc);
|
|
280
|
+
original_args[1] = (VALUE)argv;
|
|
281
|
+
original_args[2] = object;
|
|
282
|
+
|
|
283
|
+
if (impl == IMPL_ALIAS_INSTANCE || impl == IMPL_ALIAS_SINGLETON) {
|
|
284
|
+
/* Alias patching moves the original method to "cs__#{method}" */
|
|
285
|
+
cs__method = rb_funcall(known, rb_sym_brackets, 1, INT2NUM(1));
|
|
286
|
+
|
|
287
|
+
/* We may not have built the alias yet */
|
|
288
|
+
if (!RTEST(cs__method)) {
|
|
289
|
+
cs__method =
|
|
290
|
+
rb_funcall(contrast_patcher(), rb_sym_build_method_name, 2,
|
|
291
|
+
object, method);
|
|
292
|
+
}
|
|
293
|
+
original_args[3] = cs__method;
|
|
205
294
|
}
|
|
206
295
|
|
|
296
|
+
/* Enter any scopes specific to method policy */
|
|
297
|
+
rb_funcall(contrast_patcher(), rb_sym_enter_method_scope, 1, method_policy);
|
|
298
|
+
|
|
207
299
|
/* If we're not doing Contrast analysis, exit scope and treat as normal. */
|
|
208
300
|
if (!do_contrast) {
|
|
209
301
|
goto call_original;
|
|
210
302
|
}
|
|
211
303
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
304
|
+
/* Otherwise, invoke Contrast analysis. */
|
|
305
|
+
VALUE wrapped_args[7];
|
|
306
|
+
wrapped_args[0] = impl;
|
|
307
|
+
wrapped_args[1] = (VALUE)original_args;
|
|
308
|
+
wrapped_args[2] = method;
|
|
309
|
+
wrapped_args[3] = method_policy;
|
|
310
|
+
wrapped_args[4] = object;
|
|
311
|
+
wrapped_args[5] = INT2NUM(argc);
|
|
312
|
+
wrapped_args[6] = (VALUE)argv;
|
|
220
313
|
|
|
221
|
-
|
|
222
|
-
preshift = build_preshift(method_policy, object, argc, argv);
|
|
223
|
-
rescue_args[5] = preshift;
|
|
224
|
-
|
|
225
|
-
/* We wrap a call to the original method with a rescue block, and we use
|
|
226
|
-
* rb_rescue2 to capture all Exception-inheriting exceptions (and if your
|
|
227
|
-
* software is well-behaved, all exceptions should inherit from Exception.)
|
|
228
|
-
*
|
|
229
|
-
* The rescue block is responsible for doing Contrast post-call analysis
|
|
230
|
-
* in the event the original method has thrown an exception.
|
|
231
|
-
*
|
|
232
|
-
* EDGE CASES:
|
|
233
|
-
* Given how extensively we patch and instrument, this code is
|
|
234
|
-
* prone to some esoteric edge cases that are not well-documented or
|
|
235
|
-
* easy to research.
|
|
236
|
-
*
|
|
237
|
-
* There is an esoteric edge case in core Ruby, upon Thread#kill, where
|
|
238
|
-
* it raises Fixnum 8 (Qnil==8). This is an intentional choice on the
|
|
239
|
-
* part of the core Ruby devs, as blindly rescuing Thread#kill would be
|
|
240
|
-
* disastrous.
|
|
241
|
-
* A consequence of this is that Thread#kill will leak scope, if you
|
|
242
|
-
* happen to ever instrument it.
|
|
243
|
-
*
|
|
244
|
-
* If you are within a catch block, and the original function results
|
|
245
|
-
* in a throw, you will leak scope. We handle this by not instrumenting
|
|
246
|
-
* methods that do that. (Tracked in RUBY-552.)
|
|
247
|
-
*
|
|
248
|
-
* If you're thinking of cleaning this up by using rb_protect,
|
|
249
|
-
* you will catch ALL exceptions, as well as ANYTHING
|
|
250
|
-
* else that unwinds the stack. This includes fiber context switches
|
|
251
|
-
* (which are used to implement Enumerator#next) and catch/throw blocks.
|
|
252
|
-
* I spent a week debugging that so you don't have to. -ajm
|
|
253
|
-
*/
|
|
254
|
-
|
|
255
|
-
switch (impl) {
|
|
256
|
-
case IMPL_ALIAS_INSTANCE:
|
|
257
|
-
case IMPL_ALIAS_SINGLETON:
|
|
258
|
-
original_ret = rb_rescue2(
|
|
259
|
-
contrast_patch_call_original, (VALUE)original_args,
|
|
260
|
-
contrast_patch_call_rescue, (VALUE)rescue_args, rb_eException, 0);
|
|
261
|
-
break;
|
|
262
|
-
case IMPL_PREPEND:
|
|
263
|
-
original_ret = rb_rescue2(contrast_call_super, (VALUE)original_args,
|
|
264
|
-
contrast_patch_call_rescue,
|
|
265
|
-
(VALUE)rescue_args, rb_eException, 0);
|
|
266
|
-
break;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
/* If you're here, the original method did not throw an exception
|
|
270
|
-
* (or unwind the stack otherwise).
|
|
271
|
-
* If the original method threw an exception, contrast_patch_call_rescue
|
|
272
|
-
* re-raises the original exception, which unwinds the stack back to the
|
|
273
|
-
* call site. This means the rest of this function is not executed.
|
|
274
|
-
*/
|
|
275
|
-
|
|
276
|
-
/* Invoke Contrast post-call patching.
|
|
277
|
-
* Post-call patching may transform the return value,
|
|
278
|
-
* hence the assignment.
|
|
279
|
-
*/
|
|
280
|
-
transformed_ret = contrast_call_post_patch(method_policy, preshift, object,
|
|
281
|
-
original_ret, argc, argv);
|
|
282
|
-
|
|
283
|
-
/* Special case for tracking frozen sources */
|
|
284
|
-
if (transformed_ret != Qnil) {
|
|
285
|
-
ret = transformed_ret;
|
|
286
|
-
} else {
|
|
287
|
-
ret = original_ret;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/* exit scope */
|
|
291
|
-
rb_funcall(contrast_patcher(), rb_sym_exit_scope, 0);
|
|
292
|
-
|
|
293
|
-
return ret;
|
|
314
|
+
return rb_ensure(contrast_run_patches, (VALUE)wrapped_args, contrast_ensure_function, method_policy);
|
|
294
315
|
|
|
295
316
|
call_original:
|
|
296
317
|
|
|
297
318
|
/* exit scope */
|
|
298
|
-
|
|
319
|
+
contrast_ensure_function(method_policy);
|
|
299
320
|
|
|
300
321
|
switch (impl) {
|
|
301
322
|
case IMPL_ALIAS_INSTANCE:
|
|
@@ -430,6 +451,8 @@ void Init_cs__contrast_patch(void) {
|
|
|
430
451
|
rb_sym_instance_method = rb_intern("instance_method");
|
|
431
452
|
rb_sym_cs_singleton_class = rb_intern("cs__singleton_class");
|
|
432
453
|
|
|
454
|
+
rb_sym_enter_method_scope = rb_intern("enter_method_scope!");
|
|
455
|
+
rb_sym_exit_method_scope = rb_intern("exit_method_scope!");
|
|
433
456
|
|
|
434
457
|
rb_define_module_function(contrast_patcher(), "contrast_define_method",
|
|
435
458
|
contrast_patch_define_method, 3);
|
|
@@ -23,6 +23,9 @@ static VALUE rb_sym_cs_to_s;
|
|
|
23
23
|
|
|
24
24
|
static VALUE rb_sym_in_request_context;
|
|
25
25
|
|
|
26
|
+
static VALUE rb_sym_enter_method_scope;
|
|
27
|
+
static VALUE rb_sym_exit_method_scope;
|
|
28
|
+
|
|
26
29
|
static VALUE rb_sym_build_method_name;
|
|
27
30
|
static VALUE rb_sym_info_for;
|
|
28
31
|
static VALUE rb_sym_propagation_node;
|