-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
function call injection has problems when the function call involves passing variables stored in registers #3310
Comments
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Mar 24, 2023
deref'd Fix infinite recursion if escapeCheck, at some point during its recursion, creates an unreadable variable. The deeper reason for this is that we evaluate function calls in a very weird order so that we can always have stack space to store intermediate evaluation results. The variable 'value' happens to be stored in a register when we try to make the call and because of our weird evaluation strategy registers are no longer available to us when we evaluate 'value'. This is not a complete fix for the issue, the real fix would be to evaluate everything in its natural order, storing intermediate values in Delve's memory instead of the target's stack. To do this we need a mechanism to pin heap allocated objects, which at the moment does not exist. Updates go-delve#3310
derekparker
pushed a commit
that referenced
this issue
Mar 27, 2023
deref'd Fix infinite recursion if escapeCheck, at some point during its recursion, creates an unreadable variable. The deeper reason for this is that we evaluate function calls in a very weird order so that we can always have stack space to store intermediate evaluation results. The variable 'value' happens to be stored in a register when we try to make the call and because of our weird evaluation strategy registers are no longer available to us when we evaluate 'value'. This is not a complete fix for the issue, the real fix would be to evaluate everything in its natural order, storing intermediate values in Delve's memory instead of the target's stack. To do this we need a mechanism to pin heap allocated objects, which at the moment does not exist. Updates #3310
aarzilli
changed the title
reflect.Value call Type() out of memory
function call injection has problems when the function call involves passing variables stored in registers
Apr 16, 2023
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Sep 19, 2023
This commit splits expression evaluation into two parts. The first part (in pkg/proc/evalop/evalcompile.go) "compiles" as ast.Expr into a list of instructions (defined in pkg/proc/evalop/ops.go) for a stack machine (defined by `proc.(*evalStack)`). The second part is a stack machine (implemented by `proc.(*EvalScope).eval` and `proc.(*EvalScope).evalOne`) that has two modes of operation: in the main mode it executes inteructions from the list (by calling `evalOne`), in the second mode it executes the call injection protocol by calling `funcCallStep` repeatedly until it either the protocol finishes, needs more input from the stack machine (to set call arguments) or fails. This approach has several benefits: - it is now possible to remove the goroutine we use to evaluate expression and the channel used to communicate with the Continue loop. - every time we resume the target to execute the call injection protocol we need to update several local variables to match the changed state of the target, this is now done at the top level of the evaluation loop instead of being hidden inside a recurisive evaluator - using runtime.Pin to pin addresses returned by an injected call would allow us to use a more natural evaluation order for function calls, which would solve some bugs go-delve#3310, allow users to inspect values returned by a call injection go-delve#1599 and allow implementing some other features go-delve#1465. Doing this with the recursive evaluator, while keeping backwards compatibility with versions of Go that do not have runtime.Pin is very hard. However after this change we can simply conditionally change how compileFunctionCall works and add some opcodes.
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Sep 22, 2023
This commit splits expression evaluation into two parts. The first part (in pkg/proc/evalop/evalcompile.go) "compiles" as ast.Expr into a list of instructions (defined in pkg/proc/evalop/ops.go) for a stack machine (defined by `proc.(*evalStack)`). The second part is a stack machine (implemented by `proc.(*EvalScope).eval` and `proc.(*EvalScope).evalOne`) that has two modes of operation: in the main mode it executes inteructions from the list (by calling `evalOne`), in the second mode it executes the call injection protocol by calling `funcCallStep` repeatedly until it either the protocol finishes, needs more input from the stack machine (to set call arguments) or fails. This approach has several benefits: - it is now possible to remove the goroutine we use to evaluate expression and the channel used to communicate with the Continue loop. - every time we resume the target to execute the call injection protocol we need to update several local variables to match the changed state of the target, this is now done at the top level of the evaluation loop instead of being hidden inside a recurisive evaluator - using runtime.Pin to pin addresses returned by an injected call would allow us to use a more natural evaluation order for function calls, which would solve some bugs go-delve#3310, allow users to inspect values returned by a call injection go-delve#1599 and allow implementing some other features go-delve#1465. Doing this with the recursive evaluator, while keeping backwards compatibility with versions of Go that do not have runtime.Pin is very hard. However after this change we can simply conditionally change how compileFunctionCall works and add some opcodes.
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Oct 9, 2023
This commit splits expression evaluation into two parts. The first part (in pkg/proc/evalop/evalcompile.go) "compiles" as ast.Expr into a list of instructions (defined in pkg/proc/evalop/ops.go) for a stack machine (defined by `proc.(*evalStack)`). The second part is a stack machine (implemented by `proc.(*EvalScope).eval` and `proc.(*EvalScope).evalOne`) that has two modes of operation: in the main mode it executes inteructions from the list (by calling `evalOne`), in the second mode it executes the call injection protocol by calling `funcCallStep` repeatedly until it either the protocol finishes, needs more input from the stack machine (to set call arguments) or fails. This approach has several benefits: - it is now possible to remove the goroutine we use to evaluate expression and the channel used to communicate with the Continue loop. - every time we resume the target to execute the call injection protocol we need to update several local variables to match the changed state of the target, this is now done at the top level of the evaluation loop instead of being hidden inside a recurisive evaluator - using runtime.Pin to pin addresses returned by an injected call would allow us to use a more natural evaluation order for function calls, which would solve some bugs go-delve#3310, allow users to inspect values returned by a call injection go-delve#1599 and allow implementing some other features go-delve#1465. Doing this with the recursive evaluator, while keeping backwards compatibility with versions of Go that do not have runtime.Pin is very hard. However after this change we can simply conditionally change how compileFunctionCall works and add some opcodes.
derekparker
pushed a commit
that referenced
this issue
Oct 17, 2023
* proc: use stack machine to evaluate expressions This commit splits expression evaluation into two parts. The first part (in pkg/proc/evalop/evalcompile.go) "compiles" as ast.Expr into a list of instructions (defined in pkg/proc/evalop/ops.go) for a stack machine (defined by `proc.(*evalStack)`). The second part is a stack machine (implemented by `proc.(*EvalScope).eval` and `proc.(*EvalScope).evalOne`) that has two modes of operation: in the main mode it executes inteructions from the list (by calling `evalOne`), in the second mode it executes the call injection protocol by calling `funcCallStep` repeatedly until it either the protocol finishes, needs more input from the stack machine (to set call arguments) or fails. This approach has several benefits: - it is now possible to remove the goroutine we use to evaluate expression and the channel used to communicate with the Continue loop. - every time we resume the target to execute the call injection protocol we need to update several local variables to match the changed state of the target, this is now done at the top level of the evaluation loop instead of being hidden inside a recurisive evaluator - using runtime.Pin to pin addresses returned by an injected call would allow us to use a more natural evaluation order for function calls, which would solve some bugs #3310, allow users to inspect values returned by a call injection #1599 and allow implementing some other features #1465. Doing this with the recursive evaluator, while keeping backwards compatibility with versions of Go that do not have runtime.Pin is very hard. However after this change we can simply conditionally change how compileFunctionCall works and add some opcodes. * review round 1 * review round 2
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Jan 23, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Jul 30, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Aug 6, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Sep 15, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Sep 25, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Oct 1, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Oct 4, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Oct 4, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Oct 4, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
aarzilli
added a commit
to aarzilli/delve
that referenced
this issue
Oct 4, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates go-delve#3310
derekparker
pushed a commit
that referenced
this issue
Oct 4, 2024
This commit adds a new mode to call injection. If the runtime.debugPinner function is available in the target executable it obtains a pinner by calling it and then uses it to pin the pointers in the results of call injection. This allows the code for call injection to be refactored to execute the calls in the normal order, since it doesn't need to be concerned with having space on the target's memory to store intermediate values. Updates #3310
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
dlv version
)?dlv 1.20.1
go version
)?go 1.20.1
Linux fedora 6.1.18-200.fc37.x86_64
I debugged my program and when trying a
call
to get some info, delve took all my ram and my computer crashed.When limiting with
ulimit
I getfatal error: out of memory
(full stacktrace)I reproduced the issue with a minimal go program
I just put a breakpoint on
main.reflectFunc
, did a single step.Then I used
call value.Type()
What did you expect to see?
I expected an error or my
call
to workWhat did you see instead?
My computer crashed :'(
The text was updated successfully, but these errors were encountered: