Skip to content

co_annotations branch caused a crash in stackeffect() in compile.c #88057

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

Closed
larryhastings opened this issue Apr 19, 2021 · 6 comments
Closed
Labels
3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-crash A hard crash of the interpreter, possibly with a core dump

Comments

@larryhastings
Copy link
Contributor

BPO 43891
Nosy @larryhastings

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2021-04-19.23:00:10.044>
labels = ['interpreter-core', '3.10', 'type-crash']
title = 'co_annotations branch caused a crash in stackeffect() in compile.c'
updated_at = <Date 2021-04-19.23:00:38.255>
user = 'https://github.com/larryhastings'

bugs.python.org fields:

activity = <Date 2021-04-19.23:00:38.255>
actor = 'larry'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Interpreter Core']
creation = <Date 2021-04-19.23:00:10.044>
creator = 'larry'
dependencies = []
files = []
hgrepos = []
issue_num = 43891
keywords = []
message_count = 2.0
messages = ['391413', '391414']
nosy_count = 1.0
nosy_names = ['larry']
pr_nums = []
priority = 'normal'
resolution = None
stage = 'patch review'
status = 'open'
superseder = None
type = 'crash'
url = 'https://bugs.python.org/issue43891'
versions = ['Python 3.10']

@larryhastings
Copy link
Contributor Author

I'm working on a branch to implement PEP-649:

https://github.com/larryhastings/co_annotations/

Inada Naoki discovered a crash in that branch, discussed here, including steps to reproduce:

larryhastings/co_annotations#10

valgrind showed me what the problem was. stackeffect() allocates a "stack" variable, used to store pushed/popped context while iterating over the basic blocks of the function being assembled. Most of the time, the stack is way bigger than it needs to be--we allocate 4 or 5 entries and it only uses 1 or 2. But, somehow, in the co_annotations branch, the "stack" was occasionally *way too small*. As in, it allocated 66 entries (!) but used 150 (!!).

I don't understand exactly how stackeffect works, so I don't know under what circumstances it would go so deep, much less what would cause it to so severely underestimate how many entries it needed. I *did* make modifications to code generation in compile.c, so it *could* be my bug--but my changes were all much earlier in the process, and AFAIK I never touched any of the code under assemble().

Well, not until I worked around this problem, anyway. My fix: if "stack" is too small, double the size and realloc(). Certainly it makes the problem go away. That's checked in to my branch here:

larryhastings/co_annotations@63b415c

But it doesn't address the underlying bug, whatever it is.

If anybody who understands stackeffect() could take a look and figure it out? That would be neat-o keen.

@larryhastings larryhastings added 3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-crash A hard crash of the interpreter, possibly with a core dump labels Apr 19, 2021
@larryhastings
Copy link
Contributor Author

(Sorry, the name of the function is stackdepth(), not stackeffect().)

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
iritkatriel added a commit to iritkatriel/cpython that referenced this issue Sep 2, 2022
@iritkatriel
Copy link
Member

Larry, I checked out your branch to test my hypothesis but I was unable to build it. If you still can build it, then perhaps you can try adding the assertion I suggest below to see if it trips.

The size of the stack is the number of blocks, and the intention is that each block is pushed at most once. The only way I can see blocks being pushed more than once is if in stackdepth_push we have b->b_startdepth < 0 && depth < 0 && b->b_startdepth < depth. By why would depth ever be <0?

It is possible that this happened if your changes caused the bytecode to be what we call "invalid", such that new_depth is negative. Unlike the other places where we calculate a depth, there is no assertion that new_depth is >= 0. Then we have depth = new_depth, and if we loop again we reach the assertion on depth >= 0. But if this is the last iteration, then we don't check depth, we just call stackdepth_push(&sp, next, depth); after the loop ends.

If next is unvisited, then next->b_startdepth is MIN_INT. So it will be smaller than some small-negative depth.

In summary, I think it is a bug in your code, which would have been easier to find if we had
assert(new_depth >= 0); /* invalid code or bug in stackdepth() */ just after new_depth is calculated.

I will make a patch to add this assertion.

@sweeneyde
Copy link
Member

Could it be related to the discussion here about potentially switching "visited" to mean "pushed" instead of the current meaning, "popped"? This could cause overflow if multiple instructions have the same target, and so things get pushed twice before they're popped.

@sweeneyde
Copy link
Member

Never mind, I don't think that's the issue -- stackdepth already uses "visited"="pushed".

@iritkatriel
Copy link
Member

That was my first thought as well, but here depth >= 0 seems to be used as a proxy for ‘visited’. So I think the only way we could insert something more than once is if its depth is negative, but not MIN_INT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-crash A hard crash of the interpreter, possibly with a core dump
Projects
None yet
Development

No branches or pull requests

3 participants