Sitelet https://web.archive.org/web/20221109225700/https://github.com/github/codeql/pull/11114
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

Ruby: Add case string comparison barrier guard #11114

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

hmac
Copy link
Contributor

@hmac hmac commented Nov 3, 2022 •

This recognises barriers of the form

STRINGS = ["foo", "bar"]

case foo
when "some string literal"
  foo
when *["other", "strings"]
  foo
when *STRINGS
  foo
end

where the reads of foo inside each when are guarded by the comparison
of foo with the string literals.

We don't yet recognise this construct:

case foo
when "foo", "bar"
  foo
end

This is due to a limitation in the shared barrier guard logic.

This recognises barriers of the form

    STRINGS = ["foo", "bar"]

    case foo
    when "some string literal"
      foo
    when *["other", "strings"]
      foo
    when *STRINGS
      foo
    end

where the reads of `foo` inside each `when` are guarded by the comparison
of `foo` with the string literals.

We don't yet recognise this construct:

    case foo
    when "foo", "bar"
      foo
    end

This is due to a limitation in the shared barrier guard logic.
@hmac hmac mentioned this pull request Nov 3, 2022
@hmac hmac marked this pull request as ready for review Nov 4, 2022
@hmac hmac requested a review from a team as a code owner Nov 4, 2022
@calumgrant calumgrant requested a review from aibaars Nov 7, 2022

final override SplatExpr getExpr() { result = super.getExpr() }

final ExprCfgNode getOperand() { result.getExpr() = e.(SplatExpr).getOperand() }
Copy link
Contributor

@aibaars aibaars Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a class SplatExprChildMapping and change this to

Suggested change
final ExprCfgNode getOperand() { result.getExpr() = e.(SplatExpr).getOperand() }
final ExprCfgNode getLhs() { e.hasCfgChild(e.getOperand(), this, result) }

Copy link
Contributor Author

@hmac hmac Nov 7, 2022 •

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but why (the extra class)?

Copy link
Contributor Author

@hmac hmac Nov 7, 2022 •

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well regardless of why, I think that's fixed the issue below with SplatExprCfgNode.getAnOperand() not giving results for array literals. Still, in the absence of any documentation comments, it would be nice to know why.

Copy link
Contributor

@aibaars aibaars Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There may be multiple CFG nodes for the same AST node due to control flow graph splitting. The e.hasCfgChild predicate picks the CfgNode that matches the AstNode child node and is connected to the current CfgNode.

branch = true and
exists(CfgNodes::ExprNodes::CaseExprCfgNode case |
case.getValue() = testedNode and
exists(CfgNodes::ExprNodes::WhenClauseCfgNode branchNode |
Copy link
Contributor

@aibaars aibaars Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about InClauses?

Copy link
Contributor Author

@hmac hmac Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't handle in clauses. It looks much harder to be sure that an expression is guarded by an in pattern, because the patterns can be complex. I guess we'd have to check that there are no new variable bindings in the pattern or something. Anyway, I think that's something to tackle in the future.

Copy link
Contributor Author

@hmac hmac Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a commit that handles very simple in clauses where the pattern is a single string literal, such as

case foo
in "foo"
  foo
end

) {
branch = true and
exists(CfgNodes::ExprNodes::CaseExprCfgNode case |
case.getValue() = testedNode and
Copy link
Contributor

@aibaars aibaars Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also handle the case where getValue does not exist?

case
  when foo == "foo" then ...
end

Copy link
Contributor Author

@hmac hmac Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, I didn't know you could do this. This appears to already be covered by our existing StringConstCompareBarrier. I'll add a test for it.

pattern instanceof ExprNodes::StringLiteralCfgNode
or
// array literals behave weirdly in the CFG so we need to drop down to the AST level for this bit
// specifically: `SplatExprCfgNode.getOperand()` does not return results for array literals
Copy link
Contributor

@aibaars aibaars Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit surprised by this comment. It could be that there is a bug in the CFG library, but other than that getOperand should return an array literal.

Copy link
Contributor Author

@hmac hmac Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your suggested change for SplatExprCfgNode appears to have fixed this. I've pushed an update.

foo
end

if foo == "foo" or foo == "bar" # not recognised
Copy link
Contributor

@aibaars aibaars Nov 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be fixed by #11153

@aibaars
Copy link
Contributor

aibaars commented Nov 9, 2022

@hmac I trivially fixed all merge conflicts in the test cases through the GitHub UI. You probably need to refresh the expected output, though.

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

Successfully merging this pull request may close these issues.

None yet

2 participants