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

Possible unsoundness in the resource leak checker due to ownership + generics #5908

Closed
Calvin-L opened this issue May 22, 2023 · 3 comments · Fixed by #5912
Closed

Possible unsoundness in the resource leak checker due to ownership + generics #5908

Calvin-L opened this issue May 22, 2023 · 3 comments · Fixed by #5912

Comments

@Calvin-L
Copy link
Contributor

Calvin-L commented May 22, 2023

I am applying the resource leak checker to a large program. I encountered some interesting patterns.

Perhaps the most interesting: my program has a number of methods that take ownership of an object but are generic. In some sense these methods "forget" the type of the owned argument, including any must-call requirements. (In practice the owned objects are eventually passed back to a non-generic callback that closes the argument. The flow is not obviously correct and difficult to simplify, and therefore it is important to verify.)

I'm worried that the resource leak checker might be missing some bugs in my program. Consider this simplified example:

package calvin.example;

import org.checkerframework.checker.mustcall.qual.*;

class Resource implements java.io.Closeable {
    @Override
    public void close() {
    }
}

public class Driver {

    public static <T> void sneakyDrop(@Owning T value) {
    }

    public static void main(String[] args) throws Exception {
        Resource x = new Resource();
        sneakyDrop(x);
    }

}

To my surprise, the call to sneakyDrop(x) seems to satisfy the resource leak checker even though the resource x is obviously not being closed.

@kelloggm
Copy link
Contributor

Thanks for reporting this, and sorry you've encountered a problem.

To my surprise, the call to sneakyDrop(x) seems to satisfy the resource leak checker even though the resource x is obviously not being closed.

I am also surprised by this - I would expect that the Must Call Checker would issue an error when you attempt to substitute an @MustCall("close") Resource for a T.

Here is a theory about why: CLIMB-to-top inference treats T and T extends Object differently: the former is usually default to the top type, while the latter uses the regular rules for Object. In the Must Call Checker, the top type is an artificial, invisible type (@MustCallUnknown, meaning any must call obligations are permitted); the default for object by contrast is bottom (@MustCall({})). The Resource Leak Checker might be incorrectly assuming that T means T extends @MustCallUnknown Object, but if it is doing so I'd expect us to get a warning in sneakyThrows, instead.

Regardless, I'll investigate this issue and let you know when we've figured it out (probably tomorrow, though, since it's late on the east coast already).

@kelloggm
Copy link
Contributor

There was in fact a soundness bug related to @MustCallUnknown, which should be fixed by #5912. My hypothesis in my previous comment about the cause appears to have been correct: T means T extends @MustCallUnknown Object, and the checker's logic for handling obligations incorrectly treated @MustCallUnknown as @MustCall({}) when converting between two internal subcheckers (the Must Call Checker and the consistency analyzer).

@Calvin-L
Copy link
Contributor Author

Thanks a ton for the quick followup. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants