-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Detect usage of ICryptoTransform that would be thread-unsafe #956
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
Merged
hvitved
merged 9 commits into
github:master
from
raulgarciamsft:users/raulga/ICryptoTransform
Mar 1, 2019
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
7d19769
Adding a new rule for detecting usage of static objects that implemen…
raulgarciamsft fa73b84
Update .gitignore
raulgarciamsft 143b1e5
Update .gitignore
raulgarciamsft 9bb7816
Making changes based on feedback.
raulgarciamsft f8ae56a
Improving documentation
raulgarciamsft fb5f220
Merge branch 'users/raulga/ICryptoTransform' of https://github.com/ra…
raulgarciamsft 1ae1897
Fixing bugs found during Code Review.
raulgarciamsft e24ca8e
Update .gitignore
raulgarciamsft 9eca21c
Update .gitignore
raulgarciamsft File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
38 changes: 38 additions & 0 deletions
38
csharp/ql/src/Likely Bugs/ThreadUnSafeICryptoTransformBad.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| internal class TokenCacheThreadUnsafeICryptoTransformDemo | ||
| { | ||
| private static SHA256 _sha = SHA256.Create(); | ||
|
|
||
| public string ComputeHash(string data) | ||
| { | ||
| byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(data); | ||
| return Convert.ToBase64String(_sha.ComputeHash(passwordBytes)); | ||
| } | ||
| } | ||
|
|
||
| class Program | ||
| { | ||
| static void Main(string[] args) | ||
| { | ||
| int max = 1000; | ||
| Task[] tasks = new Task[max]; | ||
|
|
||
| Action<object> action = (object obj) => | ||
| { | ||
| var unsafeObj = new TokenCacheThreadUnsafeICryptoTransformDemo(); | ||
| if (unsafeObj.ComputeHash((string)obj) != "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=") | ||
| { | ||
| Console.WriteLine("**** We got incorrect Results!!! ****"); | ||
| } | ||
| }; | ||
|
|
||
| for (int i = 0; i < max; i++) | ||
| { | ||
| // hash calculated on all threads should be the same: | ||
| // ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0= (base64) | ||
| // | ||
| tasks[i] = Task.Factory.StartNew(action, "abc"); | ||
| } | ||
|
|
||
| Task.WaitAll(tasks); | ||
| } | ||
| } |
41 changes: 41 additions & 0 deletions
41
csharp/ql/src/Likely Bugs/ThreadUnSafeICryptoTransformGood.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| internal class TokenCacheThreadUnsafeICryptoTransformDemoFixed | ||
| { | ||
| // We are replacing the static SHA256 field with an instance one | ||
| // | ||
| //private static SHA256 _sha = SHA256.Create(); | ||
| private SHA256 _sha = SHA256.Create(); | ||
|
|
||
| public string ComputeHash(string data) | ||
| { | ||
| byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(data); | ||
| return Convert.ToBase64String(_sha.ComputeHash(passwordBytes)); | ||
| } | ||
| } | ||
|
|
||
| class Program | ||
| { | ||
| static void Main(string[] args) | ||
| { | ||
| int max = 1000; | ||
| Task[] tasks = new Task[max]; | ||
|
|
||
| Action<object> action = (object obj) => | ||
| { | ||
| var safeObj = new TokenCacheThreadUnsafeICryptoTransformDemoFixed(); | ||
| if (safeObj.ComputeHash((string)obj) != "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=") | ||
| { | ||
| Console.WriteLine("**** We got incorrect Results!!! ****"); | ||
| } | ||
| }; | ||
|
|
||
| for (int i = 0; i < max; i++) | ||
| { | ||
| // hash calculated on all threads should be the same: | ||
| // ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0= (base64) | ||
| // | ||
| tasks[i] = Task.Factory.StartNew(action, "abc"); | ||
| } | ||
|
|
||
| Task.WaitAll(tasks); | ||
| } | ||
| } |
38 changes: 38 additions & 0 deletions
38
csharp/ql/src/Likely Bugs/ThreadUnsafeICryptoTransform.qhelp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <!DOCTYPE qhelp PUBLIC | ||
| "-//Semmle//qhelp//EN" | ||
| "qhelp.dtd"> | ||
| <qhelp> | ||
| <overview> | ||
| <p>Classes that implement <code>System.Security.Cryptography.ICryptoTransform</code> are not thread safe.</p> | ||
| <p>This problem is caused by the way these classes are implemented using Microsoft CAPI/CNG patterns.</p> | ||
| <p>For example, when a hash class implements this interface, there would typically be an instance-specific hash object created (for example using <code>BCryptCreateHash</code> function). This object can be called multiple times to add data to the hash (for example <code>BCryptHashData</code>). Finally, a function is called that finishes the hash and returns the data (for example <code>BCryptFinishHash</code>).</p> | ||
| <p>Allowing the same hash object to be called with data from multiple threads before calling the finish function could potentially lead to incorrect results.</p> | ||
| <p>For example, if you have multiple threads hashing <code>"abc"</code> on a static hash object, you may occasionally obtain the results (incorrectly) for hashing <code>"abcabc"</code>, or face other unexpected behavior.</p> | ||
| <p>It is very unlikely somebody outside Microsoft would write a class that implements <code>ICryptoTransform</code>, and even if they do, it is likely that they will follow the same common pattern as the existing classes implementing this interface.</p> | ||
| <p>Any object that implements <code>System.Security.Cryptography.ICryptoTransform</code> should not be used in concurrent threads as the instance members of such object are also not thread safe.</p> | ||
raulgarciamsft marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <p>Potential problems may not be evident at first, but can range from explicit errors such as exceptions, to incorrect results when sharing an instance of such an object in multiple threads.</p> | ||
|
|
||
| </overview> | ||
| <recommendation> | ||
| <p>If the object is shared across instances, you should consider changing the code to use a non-static object of type <code>System.Security.Cryptography.ICryptoTransform</code> instead.</p> | ||
| <p>As an alternative, you could also look into using <code>ThreadStatic</code> attribute, but make sure you read the initialization remarks on the documentation.</p> | ||
|
|
||
| </recommendation> | ||
| <example> | ||
| <p>This example demonstrates the dangers of using a static <code>System.Security.Cryptography.ICryptoTransform</code> in a way that generates incorrect results.</p> | ||
| <sample src="ThreadUnSafeICryptoTransformBad.cs" /> | ||
|
|
||
| <p>A simple fix is to change the <code>_sha</code> field from being a static member to an instance one by removing the <code>static</code> keyword.</p> | ||
| <sample src="ThreadUnSafeICryptoTransformGood.cs" /> | ||
| </example> | ||
|
|
||
| <references> | ||
| <li> | ||
| Microsoft documentation, <a href="https://docs.microsoft.com/en-us/dotnet/api/system.threadstaticattribute?view=netframework-4.7.2">ThreadStaticAttribute Class</a>. | ||
| </li> | ||
| <li> | ||
| Stack Overflow, <a href="https://stackoverflow.com/questions/26592596/why-does-sha1-computehash-fail-under-high-load-with-many-threads">Why does SHA1.ComputeHash fail under high load with many threads?</a>. | ||
| </li> | ||
| </references> | ||
|
|
||
| </qhelp> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /** | ||
| * @name Class defines a field that uses an ICryptoTransform class in a way that would be unsafe for concurrent threads. | ||
| * @description The class has a field that directly or indirectly make use of a static System.Security.Cryptography.ICryptoTransform object. | ||
| * Using this an instance of this class in concurrent threads is dangerous as it may not only result in an error, | ||
| * but under some circumstances may also result in incorrect results. | ||
| * @kind problem | ||
| * @problem.severity warning | ||
| * @precision medium | ||
| * @id cs/thread-unsafe-icryptotransform-field-in-class | ||
| * @tags concurrency | ||
| * security | ||
| * external/cwe/cwe-362 | ||
| */ | ||
|
|
||
| import csharp | ||
|
|
||
| class ICryptoTransform extends Class { | ||
| ICryptoTransform() { | ||
| this.getABaseType*().hasQualifiedName("System.Security.Cryptography", "ICryptoTransform") | ||
| } | ||
| } | ||
|
|
||
| predicate usesICryptoTransformType( Type t ) { | ||
| exists( ICryptoTransform ict | | ||
| ict = t | ||
| or usesICryptoTransformType( t.getAChild() ) | ||
| ) | ||
| } | ||
|
|
||
| predicate hasICryptoTransformMember( Class c) { | ||
| exists( Field f | | ||
| f = c.getAMember() | ||
| and ( | ||
| exists( ICryptoTransform ict | ict = f.getType() ) | ||
| or hasICryptoTransformMember(f.getType()) | ||
| or usesICryptoTransformType(f.getType()) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| predicate hasICryptoTransformStaticMemberNested( Class c ) { | ||
| exists( Field f | | ||
| f = c.getAMember() | | ||
| hasICryptoTransformStaticMemberNested( f.getType() ) | ||
| or ( | ||
| f.isStatic() and hasICryptoTransformMember(f.getType()) | ||
| and not exists( Attribute a | ||
| | a = f.getAnAttribute() | | ||
| a.getType().getQualifiedName() = "System.ThreadStaticAttribute" | ||
| ) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| predicate hasICryptoTransformStaticMember( Class c, string msg) { | ||
| exists( Field f | | ||
| f = c.getAMember*() | ||
| and f.isStatic() | ||
| and not exists( Attribute a | ||
| | a = f.getAnAttribute() | ||
| and a.getType().getQualifiedName() = "System.ThreadStaticAttribute" | ||
| ) | ||
| and ( | ||
| exists( ICryptoTransform ict | | ||
| ict = f.getType() | ||
| and msg = "Static field " + f + " of type " + f.getType() + ", implements 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads." | ||
| ) | ||
| or | ||
| ( | ||
| not exists( ICryptoTransform ict | ict = f.getType() ) // Avoid dup messages | ||
| and exists( Type t | t = f.getType() | | ||
| usesICryptoTransformType(t) | ||
| and msg = "Static field " + f + " of type " + f.getType() + " makes usage of 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads." | ||
| ) | ||
| ) | ||
| ) | ||
| ) | ||
| or ( hasICryptoTransformStaticMemberNested(c) | ||
| and msg = "Class" + c + " implementation depends on a static object of type 'System.Security.Cryptography.ICryptoTransform' in a way that is unsafe for concurrent threads." | ||
| ) | ||
| } | ||
|
|
||
| from Class c , string s | ||
| where hasICryptoTransformStaticMember(c, s) | ||
| select c, s |
114 changes: 114 additions & 0 deletions
114
...test/query-tests/Likely Bugs/ThreadUnsafeICryptoTransform/ThreadUnsafeICryptoTransform.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| // semmle-extractor-options: /r:System.Security.Cryptography.Csp.dll /r:System.Security.Cryptography.Algorithms.dll /r:System.Security.Cryptography.Primitives.dll | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Security.Cryptography; | ||
|
|
||
| public class Nest01 | ||
| { | ||
| private readonly SHA256 _sha; | ||
|
|
||
| public Nest01() | ||
| { | ||
| _sha = SHA256.Create(); | ||
| } | ||
| } | ||
|
|
||
| public class Nest02 | ||
| { | ||
| private readonly Nest01 _n; | ||
|
|
||
| public Nest02() | ||
| { | ||
| _n = new Nest01(); | ||
| } | ||
| } | ||
|
|
||
| public class ListNonStatic | ||
| { | ||
| private List<SHA512> _shaList; | ||
|
|
||
| public ListNonStatic() | ||
| { | ||
| _shaList = new List<SHA512>(); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Positive results (classes are not thread safe) | ||
| /// </summary> | ||
| public class Nest03 | ||
| { | ||
| private static readonly Nest01 _n = new Nest01(); | ||
| } | ||
|
|
||
| public class Nest04 | ||
| { | ||
| static ListNonStatic _list = new ListNonStatic(); | ||
| } | ||
|
|
||
| public static class StaticMemberChildUsage | ||
| { | ||
| public enum DigestAlgorithm | ||
| { | ||
| SHA1, | ||
| SHA256, | ||
| } | ||
|
|
||
| private static readonly Dictionary<DigestAlgorithm, HashAlgorithm> HashMap = new Dictionary<DigestAlgorithm, HashAlgorithm> | ||
| { | ||
| { DigestAlgorithm.SHA1, SHA1.Create() }, | ||
| { DigestAlgorithm.SHA256, SHA256.Create() }, | ||
| }; | ||
| } | ||
|
|
||
| public class StaticMember | ||
| { | ||
| private static SHA1 _sha1 = SHA1.Create(); | ||
| } | ||
|
|
||
| public class IndirectStatic2 | ||
| { | ||
| static Nest02 _n = new Nest02(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Should not be flagged (thread safe) | ||
| /// </summary> | ||
|
|
||
| public class IndirectStatic | ||
| { | ||
| StaticMember tc; | ||
| } | ||
|
|
||
| public class TokenCacheFP | ||
| { | ||
| /// <summary> | ||
| /// Should be OK. Not shared between threads | ||
| /// </summary> | ||
| [ThreadStatic] | ||
| private static SHA1 _sha1 = SHA1.Create(); | ||
|
|
||
| private string ComputeHash(string password) | ||
| { | ||
| return password; | ||
| } | ||
| } | ||
|
|
||
| public class TokenCacheNonStat | ||
| { | ||
| /// <summary> | ||
| /// Should be OK. Not shared between threads | ||
| /// </summary> | ||
| private SHA1 _sha1; | ||
|
|
||
| public TokenCacheNonStat() | ||
| { | ||
| _sha1 = SHA1.Create(); | ||
| } | ||
|
|
||
| private string ComputeHash(string password) | ||
| { | ||
| return password; | ||
| } | ||
| } | ||
|
|
5 changes: 5 additions & 0 deletions
5
...uery-tests/Likely Bugs/ThreadUnsafeICryptoTransform/ThreadUnsafeICryptoTransform.expected
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| | ThreadUnsafeICryptoTransform.cs:39:14:39:19 | Nest03 | ClassNest03 implementation depends on a static object of type 'System.Security.Cryptography.ICryptoTransform' in a way that is unsafe for concurrent threads. | | ||
| | ThreadUnsafeICryptoTransform.cs:44:14:44:19 | Nest04 | ClassNest04 implementation depends on a static object of type 'System.Security.Cryptography.ICryptoTransform' in a way that is unsafe for concurrent threads. | | ||
| | ThreadUnsafeICryptoTransform.cs:49:21:49:42 | StaticMemberChildUsage | Static field HashMap of type Dictionary<DigestAlgorithm,HashAlgorithm> makes usage of 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads. | | ||
| | ThreadUnsafeICryptoTransform.cs:64:14:64:25 | StaticMember | Static field _sha1 of type SHA1, implements 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads. | | ||
| | ThreadUnsafeICryptoTransform.cs:69:14:69:28 | IndirectStatic2 | ClassIndirectStatic2 implementation depends on a static object of type 'System.Security.Cryptography.ICryptoTransform' in a way that is unsafe for concurrent threads. | |
1 change: 1 addition & 0 deletions
1
...t/query-tests/Likely Bugs/ThreadUnsafeICryptoTransform/ThreadUnsafeICryptoTransform.qlref
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Likely Bugs/ThreadUnsafeICryptoTransform.ql |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.