Skip to content

Conversation

@hvitved
Copy link
Contributor

@hvitved hvitved commented Feb 26, 2019

In the precense of multiple core libraries, getAThrownException() would return
multiple copies of the same exception, say System.OverflowException, one for each
core library. With this change we try to identify which core library a given control
flow element was compiled against, and only return the corresponding version.

In the precense of multiple core libraries, `getAThrownException()` would return
multiple copies of the same exception, say `System.OverflowException`, one for each
core library. With this change we try to identify which core library a given control
flow element was compiled against, and only return the corresponding version.
@hvitved hvitved added the C# label Feb 26, 2019
@hvitved hvitved requested a review from calumgrant February 26, 2019 14:16
* core type inside assembly `a`.
*/
pragma[noinline]
private predicate assemblyCompiledWithCoreLib(Assembly a, Assembly core) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@calumgrant : Can you think of a better way to do this?

@hvitved
Copy link
Contributor Author

hvitved commented Feb 26, 2019

Performance unchanged by this change (internal link): https://git.semmle.com/gist/tom/09129553dee65e843c2376651987ce33.

@hvitved hvitved marked this pull request as ready for review February 26, 2019 19:49
@hvitved hvitved requested a review from a team as a code owner February 26, 2019 19:49
@hvitved hvitved changed the base branch from master to rc/1.20 March 2, 2019 14:22
@hvitved hvitved added this to the 1.20 milestone Mar 2, 2019
@calumgrant
Copy link
Contributor

I have 2 other ideas, which may well prove to be worse than this, but I thought I would put them here.

  1. Canonicalise the exception types, so System.NullReferenceException would always refer to a single entity, picking them according to some priority. So you could get a method throwing the wrong type but I don't think that will actually matter in practise.
  2. Instead of looking at all type mentions, only look at attributes. Whilst object is canonical and cannot be used, most assemblies will use attributes, so look at the defining assembly of System.Attribute.

@hvitved
Copy link
Contributor Author

hvitved commented Mar 4, 2019

  • Canonicalise the exception types, so System.NullReferenceException would always refer to a single entity, picking them according to some priority. So you could get a method throwing the wrong type but I don't think that will actually matter in practise.

This would require an extractor change, and it seems to me a bit ad hoc to treat exception types specially.

  • Instead of looking at all type mentions, only look at attributes. Whilst object is canonical and cannot be used, most assemblies will use attributes, so look at the defining assembly of System.Attribute.

That might work, but would you prefer that for better performance of higher precision?

@calumgrant
Copy link
Contributor

  • Canonicalise the exception types, so System.NullReferenceException would always refer to a single entity, picking them according to some priority. So you could get a method throwing the wrong type but I don't think that will actually matter in practise.

This would require an extractor change, and it seems to me a bit ad hoc to treat exception types specially.

Not necessarily. You could use QL to create something like

class CanonicalType extends ValueOrRefType {
  CanonicalType() {
    not exists(ValueOrRefType t | this.getQualifiedName() = t.getQualifiedName() and
      t.getLocation().(Assembly).getName() > this.getLocation().(Assembly).getName()
    )
  }
}

Class getAThrownException() {
  result instanceof CanonicalType and
  ...
}
  • Instead of looking at all type mentions, only look at attributes. Whilst object is canonical and cannot be used, most assemblies will use attributes, so look at the defining assembly of System.Attribute.

That might work, but would you prefer that for better performance of higher precision?

It might be a little simpler and faster. Don't know.

@hvitved
Copy link
Contributor Author

hvitved commented Mar 5, 2019

Not necessarily. You could use QL to create something like

Makes sense. My only concern is that we would then also have to canonicalize explicitly thrown exceptions, throw new Exception(), where we can now just use the type produced directly by the extractor.

@calumgrant
Copy link
Contributor

Not necessarily. You could use QL to create something like

Makes sense. My only concern is that we would then also have to canonicalize explicitly thrown exceptions, throw new Exception(), where we can now just use the type produced directly by the extractor.

Yes, getAThrownException() for throw expressions would need special treatment.

I wonder if it could be the case that a library throws netstandard::System.Exception, and another module catches corlib::System.Exception. In that case, you'd probably want to match up the two types anyway. I'm not sure how often this occurs in practice.

@calumgrant
Copy link
Contributor

But possibly, CanonicalType might be a useful concept to use here.

When determining which core library a "tried control flow element" is compiled against,
first look at exceptions caught by the surrounding `try` block, then look at assembly
attributes, and finally choose (randomly) the core library with the highest lexicographic
order.
Copy link
Contributor

@calumgrant calumgrant left a comment

Choose a reason for hiding this comment

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

Approach LGTM. I guess this is quite hard to qltest, but I'd be interested in seeing the effect on performance.

private Assembly getCoreLib() { assemblyCompiledWithCoreLib(this.getAssembly(), result) }
private CoreLib getCoreLibFromACatchClause() {
exists(SpecificCatchClause scc | scc = try.getACatchClause() |
result = scc.getCaughtExceptionType().getABaseType*().getALocation()
Copy link
Contributor

Choose a reason for hiding this comment

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

One thing that might go a bit wrong here is that you'll end up querying ObjectType.getALocation() which will in fact return all CoreLibs. So perhaps

result = scc.getCaughtExceptionType().getBaseClass*().(SystemExceptionClass).getALocation()

Copy link
Contributor

Choose a reason for hiding this comment

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

Also getBaseClass

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's a clear oversight, good spot!

strictcount(stm.getSystemAssembly()) = 1
)
private predicate assemblyCompiledWithCoreLib(Assembly a, CoreLib core) {
a.getAnAttribute().getType().getABaseType*().getALocation() = core
Copy link
Contributor

Choose a reason for hiding this comment

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

core = a.getAnAttribute().getType().getBaseClass*().(SystemAttributeClass).getALocation()

otherwise we'll end up getting the location of ObjectType which will be all of them.

Also note getBaseClass() as we don't care about interfaces.

Copy link
Contributor

@calumgrant calumgrant Mar 5, 2019

Choose a reason for hiding this comment

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

I just remembered that only source assemblies have attributes (due to extractor limitations). I don't think this affects anything here but is worth bearing in mind.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Again, good spot :-)

@calumgrant calumgrant merged commit 4bdcfec into github:rc/1.20 Mar 6, 2019
@hvitved hvitved deleted the csharp/cfg/get-a-thrown-exception branch March 6, 2019 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants