-
Notifications
You must be signed in to change notification settings - Fork 439
Fix two memory corruption bugs when modifying SwiftSyntax nodes #1708
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
Conversation
@swift-ci Please test |
54c0a8a
to
567d1d8
Compare
@swift-ci Please test |
@swift-ci Please test Windows |
059ec6a
to
de3d08f
Compare
@swift-ci Please test |
1 similar comment
@swift-ci Please test |
Sources/SwiftSyntax/SyntaxData.swift
Outdated
/// - newChildArena: The arena in which `newChild` resides. | ||
/// - arena: The arena in which the new node will be allocated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the names no longer match up 😅 newChild
is actually with
in terms of the API, and newChildArena == rawNodeArena
. arena == allocationArena
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you generate this comment using Xcode (Cmd-Option-/), it will use the internal parameter names instead of the external ones 🤷🏽
Sources/SwiftSyntax/SyntaxData.swift
Outdated
return replacingSelf(newRaw, arena: arena) | ||
func replacingChild(at index: Int, with newChild: RawSyntax?, rawNodeArena: SyntaxArena?, allocationArena: SyntaxArena) -> SyntaxData { | ||
if let newChild { | ||
precondition(newChild.arena === rawNodeArena) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this not just be newChild?.arena == rawNodeArena
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to
precondition(newChild?.arena === rawNodeArena || newChild == nil)
We don’t hit the newChild == nil && rawNodeArena != nil
case at the moment AFAICT but there’s nothing really wrong with that, so I wanted to allow it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems odd to me to allow it, they're supposed to be the same so if newChild
is nil
and rawNodeArena
isn't that would suggest something strange is going on.
3bcc618
to
97cd6ed
Compare
@swift-ci Please test |
c17d2de
to
928f967
Compare
@swift-ci Please test |
928f967
to
875a3ad
Compare
@swift-ci Please test |
@swift-ci Please test |
@swift-ci Please test Windows |
1 similar comment
@swift-ci Please test Windows |
3b0e9a3
to
1357c37
Compare
@swift-ci Please test |
@swift-ci Please test Windows |
Sources/SwiftSyntax/SyntaxData.swift
Outdated
static func forRoot(_ raw: RawSyntax) -> SyntaxData { | ||
SyntaxData(raw, info: .root(.init(arena: raw.arena))) | ||
/// | ||
/// `arena` must be the arena in which `raw` is allocated. It is passed to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arena
is rawNodeArena
now 😅
Sources/SwiftSyntax/SyntaxData.swift
Outdated
func replacingSelf(_ newRaw: RawSyntax, arena: SyntaxArena) -> SyntaxData { | ||
/// - newRaw: The node that should replace `self` | ||
/// - rawNodeArena: The arena in which `newRaw` resides | ||
/// - arena: The arena in which new nodes should be allocated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this arena
is allocationArena
1357c37
to
080b216
Compare
@swift-ci Please test |
The `arena` parameter must be the same one that `raw` is allocated in and ensures that the arena of `raw` doesn’t get deallocated before the `SyntaxData` can retain it. This removes the need for a few `withExtendedLifetime` calls that we had earlier.
…e-maturely deallocated Previously, depending on the optimization level, when changing a nodes’s property, it could happen that `value` got de-allocated (because only `value.raw` was being accessed), deallocating the arena that `value.raw` lives in, resulting in freed memory access.
The `arena` parameter to `replacingSelf` and `replacingChild` served two purposes: To keep the arena of `raw`/`newChild` alive and to specify which arena new nodes should be allocated in. In almost all cases, these areneas coincided but for `SyntaxRewriter.rewrite` they diverged, causing an arena to be added as a child arena after it itself has children, causing a precondition failure. Split these different purposes of arenas into two different parameters.
We were already doing this for layout nodes but we forgot to do it for collection nodes.
080b216
to
2441d19
Compare
@swift-ci Please test |
@swift-ci please test windows |
This PR fixes two different memory corruption bugs when modifying SwiftSyntax nodes
SyntaxData.forRoot
takes aRawSyntax
, which holds a reference to itsSyntaxArena
butRawSyntax
doesn’t retain its arena. We were already taking care of this in multiple places by adding awithExtendedLifetime
for the arena around it but missed a case inSyntaxData.replacingSelf
. Add an explicitarena
parameter to that function to make sure the arena is always alivereplacingChild
function took aRawSyntax
, which again doesn’t retain its arena. When changing any syntax node’s child, we have avalue
, which is aSyntax
node but all we did in the setter was to passvalue.raw
intoreplacingChild
. This left an opportunity for the optimizer to deallocate theSyntax
node (and thus it’s arena) before the call toreplacingChild
because all we needed from hat node was theRawSyntax
. To fix this, add another version ofreplacingChild
that takes aSyntaxData
(which does retain the arena) and use that instead.I measured performance of
swift-format
with this change applied and did not see a performance regression when linting swift-syntax.rdar://109860357