Skip to content
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

AstBuilder::copy is unsound #3483

Open
overlookmotel opened this issue May 31, 2024 · 8 comments
Open

AstBuilder::copy is unsound #3483

overlookmotel opened this issue May 31, 2024 · 8 comments
Labels
C-bug Category - Bug P-high Priority - High

Comments

@overlookmotel
Copy link
Collaborator

overlookmotel commented May 31, 2024

pub fn copy<T>(&self, src: &T) -> T {
// SAFETY:
// This should be safe as long as `src` is an reference from the allocator.
// But honestly, I'm not really sure if this is safe.
#[allow(unsafe_code)]
unsafe {
std::mem::transmute_copy(src)
}
}

This method can be used correctly when a node is being removed from AST anyway, but there are multiple ways in which it can lead to unsound situations:

Aliasing violation:

fn do_bad_stuff(stmt: &Statement, ast: &AstBuilder) {
  if let Statement::BlockStatement(block) = stmt {
    let box1 = ast.copy(block);
    let box2 = ast.copy(block);
    // We now have two `Box<BlockStatement>` pointing to same `BlockStatement`
  }
}

Mutation through immutable ref:

fn do_bad_stuff2(stmt: &Statement, ast: &AstBuilder) {
  if let Statement::BlockStatement(block) = stmt {
    let block = ast.copy(block);
    // We can mutate, even though we only got an immutable ref
    block.body.clear();
  }
}

The double-free which #3481 fixes was likely caused by using copy.

We could make the method unsafe and caller will have to ensure it's only used when safe to do so. However, personally I think we should remove the API entirely because (1) it is difficult to reason about when it's safe to use and is therefore a footgun and (2) the performance hit from using safe alternatives is very minimal.

Unfortunately the alternatives are likely take or mem::replace which are not so ergonomic.

@Boshen
Copy link
Member

Boshen commented Jun 20, 2024

@Dunqing Please don't use this API from now on 😁 .

Use move_xxx APIs instead.

@overlookmotel
Copy link
Collaborator Author

I don't think it's necessarily high priority to fix this. I just wanted to request that we don't introduce more uses of it now, as it will make it more work to remove it later.

Dunqing pushed a commit that referenced this issue Jun 25, 2024
As discussed in #3483, `AstBuilder::copy` is unsound. It's going to be a
hard slog removing all uses of it. This PR adds a comment to please not
introduce any further usages of it in meantime.
@rzvxa
Copy link
Collaborator

rzvxa commented Aug 7, 2024

@overlookmotel Can we use the CloneIn trait over this?

@overlookmotel
Copy link
Collaborator Author

I think CloneIn is something different. Usually ast.copy is used when we want to extract a node from AST, and insert it somewhere else. Cloning would be very expensive if the node is the tip of a deep tree, and isn't necessary.

@rzvxa
Copy link
Collaborator

rzvxa commented Aug 8, 2024

I think CloneIn is something different. Usually ast.copy is used when we want to extract a node from AST, and insert it somewhere else. Cloning would be very expensive if the node is the tip of a deep tree, and isn't necessary.

If we just want to take the sub-tree out and put it somewhere else, We should be able to use the move method instead. For all other cases if we don't clone the sub-tree and actually copy it in 2 places how can we prevent aliasing?

@overlookmotel
Copy link
Collaborator Author

Yes, that's true, if you want to duplicate the sub-tree and put it in AST in 2 places, it must be cloned or it's UB. But all the places I saw ast.copy being used, it was to take it out of one place, and put it back in another.

@rzvxa
Copy link
Collaborator

rzvxa commented Aug 9, 2024

So what is stopping us from replacing all of our usages with the move method? Wouldn't it be better if we deprecate this unsound method?

@overlookmotel
Copy link
Collaborator Author

So what is stopping us from replacing all of our usages with the move method?

Nothing! We just haven't done it yet.

Unfortunately, using move methods is quite unergonomic - but such is the ways of Rust.

I would be really keen to see the back of this unsound method. I remain quite anxious about the soundness of Traverse, and would like to run Miri over the transformer to test it. But right now there's no point doing that because Miri is likely to go ape over all the ast.copys, and that'll obscure any other problem.

Boshen pushed a commit that referenced this issue Aug 15, 2024
…copy` as an unsafe function (#4907)

`AstBuilder::copy` is completely unsound (#3483), and we need to remove it. Make it an `unsafe` function to discourage any further usage of it in meantime.
overlookmotel added a commit that referenced this issue Aug 15, 2024
…copy` as an unsafe function (#4907)

`AstBuilder::copy` is completely unsound (#3483), and we need to remove it. Make it an `unsafe` function to discourage any further usage of it in meantime.
overlookmotel added a commit that referenced this issue Aug 15, 2024
…r` transform (#4913)

Remove one unnecessary `ast.copy` call from `NullishCoalescingOperator` transform (towards #3483).
overlookmotel added a commit that referenced this issue Aug 15, 2024
…copy` as an unsafe function (#4907)

`AstBuilder::copy` is completely unsound (#3483), and we need to remove it. Make it an `unsafe` function to discourage any further usage of it in meantime.
overlookmotel added a commit that referenced this issue Aug 15, 2024
…copy` as an unsafe function (#4907)

`AstBuilder::copy` is completely unsound (#3483), and we need to remove it. Make it an `unsafe` function to discourage any further usage of it in meantime.
overlookmotel added a commit that referenced this issue Aug 15, 2024
…copy` as an unsafe function (#4907)

`AstBuilder::copy` is completely unsound (#3483), and we need to remove it. Make it an `unsafe` function to discourage any further usage of it in meantime.
@Boshen Boshen added this to the Transformer Milestone 2 milestone Aug 29, 2024
Boshen pushed a commit that referenced this issue Sep 9, 2024
Revert the changes to `oxc_traverse` made in #5577.

Those changes may well have been good, but...

The soundness of `Traverse` is a finely balanced thing, and the pointer gymnastics are subtle, so I think it's best to be extremely conservative about changes. I last ran Miri on it with the original formulation (`as *mut _` etc) and it passed. Currently it's not possible to usefully run Miri on the transformer because it contains known unsound code (#3483). So until we're able to check soundness with Miri, I think it's best to avoid changes, as it'd be easy to trigger UB unintentionally.
@Boshen Boshen removed this from the Transformer Milestone 2 milestone Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category - Bug P-high Priority - High
Projects
None yet
Development

No branches or pull requests

3 participants