contrast-agent 3.8.5 → 3.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|