mirror of
https://github.com/github/codeql.git
synced 2026-05-03 12:45:27 +02:00
Merge pull request #235 from gagliardetto/bad-unsafe
Query to find wrong uses of package "unsafe"
This commit is contained in:
49
ql/src/experimental/Unsafe/UnsafeUsageBad.go
Normal file
49
ql/src/experimental/Unsafe/UnsafeUsageBad.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
|
||||
func badArrays() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// The declared `leaking` contains data from `harmless`
|
||||
// plus the data from `secret`;
|
||||
// (notice we read more than the length of harmless)
|
||||
var leaking = (*[8 + 9]byte)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func badIndexExpr() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret.
|
||||
// NOTE: unsafe.Pointer(&harmless) != unsafe.Pointer(&harmless[2])
|
||||
// Even tough harmless and leaking have the same size,
|
||||
// the new variable `leaking` will contain data starting from
|
||||
// the address of the 3rd element of the `harmless` array,
|
||||
// and continue for 8 bytes, going out of the boundaries of
|
||||
// `harmless` and crossing into the memory occupied by `secret`.
|
||||
var leaking = (*[8]byte)(unsafe.Pointer(&harmless[2])) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
28
ql/src/experimental/Unsafe/UnsafeUsageOK.go
Normal file
28
ql/src/experimental/Unsafe/UnsafeUsageOK.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
|
||||
func good0() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret without overflowing to secret;
|
||||
// the new `target` variable contains only data
|
||||
// inside the bounds of `harmless`, without overflowing
|
||||
// into the memory occupied by `secret`.
|
||||
var target = (*[8]byte)(unsafe.Pointer(&harmless)) // OK
|
||||
|
||||
fmt.Println(string((*target)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
46
ql/src/experimental/Unsafe/WrongUsageOfUnsafe.qhelp
Normal file
46
ql/src/experimental/Unsafe/WrongUsageOfUnsafe.qhelp
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
The package `unsafe` provides operations that step outside the type safety
|
||||
guarantees normally provided inside Go programs. This leaves room for
|
||||
errors in the usage of the `unsafe` package that are not caught by the compiler.
|
||||
</p>
|
||||
<p>
|
||||
One possible error is type casting between types of different sizes, that
|
||||
can result in reads to memory locations that are outside the bounds of
|
||||
the original target buffer, and also unexpected values.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
The basic recommendation is to avoid usage of the package `unsafe`. If that is
|
||||
not an option, you should always check the size of types you cast your data
|
||||
to/from to make sure they won't result in reading outside of the intended bounds.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
The first example explores a few scenarios of read out of bounds because of an
|
||||
erroneous type casting done with <code>unsafe.Pointer</code>.
|
||||
</p>
|
||||
<sample src="UnsafeUsageBad.go"/>
|
||||
<p>
|
||||
The second example show an example of correct type casting done with <code>unsafe.Pointer</code>.
|
||||
</p>
|
||||
<sample src="UnsafeUsageOK.go"/>
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://medium.com/a-journey-with-go/go-what-is-the-unsafe-package-d2443da36350">Go: What is the Unsafe Package?</a>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://medium.com/a-journey-with-go/go-cast-vs-conversion-by-example-26e0ef3003f0">Go: Cast vs Conversion by Example</a>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://dev.to/jlauinger/exploitation-exercise-with-unsafe-pointer-in-go-information-leak-part-1-1kga">Exploitation Exercise with unsafe.Pointer in Go: Information Leak (Part 1)</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
176
ql/src/experimental/Unsafe/WrongUsageOfUnsafe.ql
Normal file
176
ql/src/experimental/Unsafe/WrongUsageOfUnsafe.ql
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @name Wrong usage of package unsafe
|
||||
* @description Casting between types with different memory sizes can produce reads to
|
||||
* memory locations that are after the target buffer, and/or unexpected values.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/wrong-usage-of-unsafe
|
||||
* @tags security
|
||||
* external/cwe/cwe-119
|
||||
* external/cwe/cwe-126
|
||||
*/
|
||||
|
||||
import go
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/*
|
||||
* Returns the type after all aliases, named types, and pointer
|
||||
* types have been replaced with the actual underlying type.
|
||||
*/
|
||||
|
||||
Type getFinalType(Type typ) { result = getBaseType*(typ.getUnderlyingType()).getUnderlyingType() }
|
||||
|
||||
/*
|
||||
* Returns the base type of a PointerType;
|
||||
* if the provided type is not a pointer,
|
||||
* then the original type is returned.
|
||||
*/
|
||||
|
||||
Type getBaseType(Type typ) {
|
||||
result = getBaseType*(typ.(PointerType).getBaseType*())
|
||||
or
|
||||
result = typ
|
||||
}
|
||||
|
||||
/* A conversion to a `unsafe.Pointer` */
|
||||
class ConversionToUnsafePointer extends DataFlow::TypeCastNode {
|
||||
ConversionToUnsafePointer() { getFinalType(getType()) instanceof UnsafePointerType }
|
||||
}
|
||||
|
||||
/* Type casting from a `unsafe.Pointer`.*/
|
||||
class UnsafeTypeCastingConf extends TaintTracking::Configuration {
|
||||
UnsafeTypeCastingConf() { this = "UnsafeTypeCastingConf" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, ConversionToUnsafePointer conv) { source = conv }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::TypeCastNode ca) {
|
||||
ca.getOperand().getType() instanceof UnsafePointerType and
|
||||
sink = ca
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
}
|
||||
|
||||
/*
|
||||
* Type casting from a shorter array to a longer array
|
||||
* through the use of unsafe pointers.
|
||||
*/
|
||||
|
||||
predicate castShortArrayToLongerArray(
|
||||
DataFlow::PathNode source, DataFlow::PathNode sink, string message
|
||||
) {
|
||||
exists(
|
||||
UnsafeTypeCastingConf cfg, DataFlow::TypeCastNode castBig, ConversionToUnsafePointer castLittle,
|
||||
ArrayType arrTo, ArrayType arrFrom, int arrFromSize
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), castLittle) and
|
||||
cfg.isSink(sink.getNode(), castBig) and
|
||||
arrTo = getFinalType(castBig.getType()) and
|
||||
(
|
||||
// Array (whole) to array:
|
||||
// The `unsafe.Pointer` expression is on the array
|
||||
// (e.g. unsafe.Pointer(&someArray)),
|
||||
// meaning that the pointer points to the start of the array:
|
||||
arrFrom = getFinalType(castLittle.getOperand().getType()) and
|
||||
arrFromSize = arrFrom.getLength() and
|
||||
message = "Dangerous array type casting to " + arrTo.pp() + " from " + arrFrom.pp()
|
||||
or
|
||||
// Piece of array (starting from an index), to array:
|
||||
// The `unsafe.Pointer` expression can also point to a specific
|
||||
// element of an array
|
||||
// (e.g. unsafe.Pointer(&someArray[2])),
|
||||
// which will be the starting point in memory for the newly cast
|
||||
// variable.
|
||||
exists(DataFlow::ElementReadNode indexExpr |
|
||||
indexExpr = castLittle.getOperand().(DataFlow::AddressOperationNode).getOperand() and
|
||||
// The `arrFrom` is the base of the index expression:
|
||||
arrFrom = indexExpr.getBase().getType() and
|
||||
// Calculate the size of the `arrFrom`:
|
||||
arrFromSize = arrFrom.getLength() - indexExpr.getIndex().getIntValue() and
|
||||
message =
|
||||
"Dangerous array type casting to " + arrTo.pp() + " from an index expression (" +
|
||||
arrFrom.pp() + ")[" + indexExpr.getIndex().getIntValue() + "]" +
|
||||
" (the destination type is " + (arrTo.getLength() - arrFromSize) + " elements longer)"
|
||||
)
|
||||
) and
|
||||
arrTo.getLength() > arrFromSize
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Type casting from a type to an array
|
||||
* through the use of unsafe pointers.
|
||||
*/
|
||||
|
||||
predicate castTypeToArray(DataFlow::PathNode source, DataFlow::PathNode sink, string message) {
|
||||
exists(
|
||||
UnsafeTypeCastingConf cfg, DataFlow::TypeCastNode castBig, ConversionToUnsafePointer castLittle,
|
||||
ArrayType arrTo, Type typeFrom
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), castLittle) and
|
||||
cfg.isSink(sink.getNode(), castBig) and
|
||||
arrTo = getFinalType(castBig.getType()) and
|
||||
not typeFrom.getUnderlyingType() instanceof ArrayType and
|
||||
not typeFrom instanceof PointerType and
|
||||
not castLittle
|
||||
.getOperand()
|
||||
.(DataFlow::AddressOperationNode)
|
||||
.getOperand()
|
||||
.(DataFlow::ElementReadNode)
|
||||
.getBase()
|
||||
.getType() instanceof ArrayType and
|
||||
typeFrom = getFinalType(castLittle.getOperand().getType()) and
|
||||
message = "Dangerous type up-casting to " + arrTo.pp() + " from " + typeFrom
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Type casting between numeric types that have different bit sizes
|
||||
* through the use of unsafe pointers.
|
||||
*/
|
||||
|
||||
predicate castDifferentBitSizeNumbers(
|
||||
DataFlow::PathNode source, DataFlow::PathNode sink, string message
|
||||
) {
|
||||
exists(
|
||||
UnsafeTypeCastingConf cfg, DataFlow::TypeCastNode castBig, ConversionToUnsafePointer castLittle,
|
||||
NumericType numTo, NumericType numFrom
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), castLittle) and
|
||||
cfg.isSink(sink.getNode(), castBig) and
|
||||
numTo = getFinalType(castBig.getType()) and
|
||||
numFrom = getFinalType(castLittle.getOperand().getType()) and
|
||||
// TODO: also consider cast from uint to int?
|
||||
getNumericTypeSize(numTo) != getNumericTypeSize(numFrom) and
|
||||
// Exclude casts to UintptrType (which is still a pointer):
|
||||
not numTo instanceof UintptrType and
|
||||
message = "Dangerous numeric type casting to " + numTo.getName() + " from " + numFrom.getName()
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 if the NumericType is one of the numeric
|
||||
* types that have architecture-specific bit sizes.
|
||||
*/
|
||||
|
||||
int getNumericTypeSize(NumericType typ) {
|
||||
// If the numeric types have arch-specific
|
||||
// bit sizes, then set the size to 0 to distinguish
|
||||
// it from others.
|
||||
not exists(typ.getSize()) and
|
||||
result = 0
|
||||
or
|
||||
result = typ.getSize()
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, string message
|
||||
where
|
||||
castShortArrayToLongerArray(source, sink, message) or
|
||||
castTypeToArray(source, sink, message) or
|
||||
castDifferentBitSizeNumbers(source, sink, message)
|
||||
select sink.getNode(), source, sink, "$@.", source.getNode(), message
|
||||
61
ql/test/experimental/Unsafe/WrongUsageOfUnsafe.expected
Normal file
61
ql/test/experimental/Unsafe/WrongUsageOfUnsafe.expected
Normal file
@@ -0,0 +1,61 @@
|
||||
edges
|
||||
| WrongUsageOfUnsafe.go:17:24:17:48 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:17:13:17:49 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:34:24:34:51 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:34:13:34:52 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:55:24:55:51 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:55:13:55:52 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:77:27:77:54 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:77:16:77:55 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:93:20:93:44 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:93:13:93:45 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:111:31:111:58 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:111:16:111:59 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:129:31:129:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:129:16:129:56 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:149:31:149:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:149:16:149:56 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:166:33:166:57 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:166:16:166:58 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:189:31:189:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:189:16:189:56 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:211:31:211:60 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:211:16:211:61 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:227:31:227:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:236:21:236:23 | definition of req : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:236:21:236:23 | definition of req : unsafe.Pointer | WrongUsageOfUnsafe.go:243:9:243:27 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:256:28:256:52 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:256:16:256:53 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:274:25:274:49 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:274:16:274:50 | type conversion |
|
||||
| WrongUsageOfUnsafe.go:292:23:292:47 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:292:16:292:48 | type conversion |
|
||||
nodes
|
||||
| WrongUsageOfUnsafe.go:17:13:17:49 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:17:24:17:48 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:34:13:34:52 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:34:24:34:51 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:55:13:55:52 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:55:24:55:51 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:77:16:77:55 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:77:27:77:54 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:93:13:93:45 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:93:20:93:44 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:111:16:111:59 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:111:31:111:58 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:129:16:129:56 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:129:31:129:55 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:149:16:149:56 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:149:31:149:55 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:166:16:166:58 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:166:33:166:57 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:189:16:189:56 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:189:31:189:55 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:211:16:211:61 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:211:31:211:60 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:227:31:227:55 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:236:21:236:23 | definition of req : unsafe.Pointer | semmle.label | definition of req : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:243:9:243:27 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:256:16:256:53 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:256:28:256:52 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:274:16:274:50 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:274:25:274:49 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
| WrongUsageOfUnsafe.go:292:16:292:48 | type conversion | semmle.label | type conversion |
|
||||
| WrongUsageOfUnsafe.go:292:23:292:47 | type conversion : unsafe.Pointer | semmle.label | type conversion : unsafe.Pointer |
|
||||
#select
|
||||
| WrongUsageOfUnsafe.go:77:16:77:55 | type conversion | WrongUsageOfUnsafe.go:77:27:77:54 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:77:16:77:55 | type conversion | $@. | WrongUsageOfUnsafe.go:77:27:77:54 | type conversion | Dangerous array type casting to [8]uint8 from an index expression ([8]uint8)[2] (the destination type is 2 elements longer) |
|
||||
| WrongUsageOfUnsafe.go:111:16:111:59 | type conversion | WrongUsageOfUnsafe.go:111:31:111:58 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:111:16:111:59 | type conversion | $@. | WrongUsageOfUnsafe.go:111:31:111:58 | type conversion | Dangerous array type casting to [17]uint8 from an index expression ([8]uint8)[0] (the destination type is 9 elements longer) |
|
||||
| WrongUsageOfUnsafe.go:129:16:129:56 | type conversion | WrongUsageOfUnsafe.go:129:31:129:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:129:16:129:56 | type conversion | $@. | WrongUsageOfUnsafe.go:129:31:129:55 | type conversion | Dangerous array type casting to [17]uint8 from [8]uint8 |
|
||||
| WrongUsageOfUnsafe.go:149:16:149:56 | type conversion | WrongUsageOfUnsafe.go:149:31:149:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:149:16:149:56 | type conversion | $@. | WrongUsageOfUnsafe.go:149:31:149:55 | type conversion | Dangerous array type casting to [17]uint8 from [8]uint8 |
|
||||
| WrongUsageOfUnsafe.go:166:16:166:58 | type conversion | WrongUsageOfUnsafe.go:166:33:166:57 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:166:16:166:58 | type conversion | $@. | WrongUsageOfUnsafe.go:166:33:166:57 | type conversion | Dangerous array type casting to [17]string from [8]string |
|
||||
| WrongUsageOfUnsafe.go:189:16:189:56 | type conversion | WrongUsageOfUnsafe.go:189:31:189:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:189:16:189:56 | type conversion | $@. | WrongUsageOfUnsafe.go:189:31:189:55 | type conversion | Dangerous type up-casting to [17]uint8 from struct type |
|
||||
| WrongUsageOfUnsafe.go:211:16:211:61 | type conversion | WrongUsageOfUnsafe.go:211:31:211:60 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:211:16:211:61 | type conversion | $@. | WrongUsageOfUnsafe.go:211:31:211:60 | type conversion | Dangerous array type casting to [17]uint8 from [8]uint8 |
|
||||
| WrongUsageOfUnsafe.go:243:9:243:27 | type conversion | WrongUsageOfUnsafe.go:227:31:227:55 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:243:9:243:27 | type conversion | $@. | WrongUsageOfUnsafe.go:227:31:227:55 | type conversion | Dangerous array type casting to [17]uint8 from [8]uint8 |
|
||||
| WrongUsageOfUnsafe.go:256:16:256:53 | type conversion | WrongUsageOfUnsafe.go:256:28:256:52 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:256:16:256:53 | type conversion | $@. | WrongUsageOfUnsafe.go:256:28:256:52 | type conversion | Dangerous array type casting to [4]int64 from [1]int64 |
|
||||
| WrongUsageOfUnsafe.go:274:16:274:50 | type conversion | WrongUsageOfUnsafe.go:274:25:274:49 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:274:16:274:50 | type conversion | $@. | WrongUsageOfUnsafe.go:274:25:274:49 | type conversion | Dangerous numeric type casting to int64 from int8 |
|
||||
| WrongUsageOfUnsafe.go:292:16:292:48 | type conversion | WrongUsageOfUnsafe.go:292:23:292:47 | type conversion : unsafe.Pointer | WrongUsageOfUnsafe.go:292:16:292:48 | type conversion | $@. | WrongUsageOfUnsafe.go:292:23:292:47 | type conversion | Dangerous numeric type casting to int from int8 |
|
||||
300
ql/test/experimental/Unsafe/WrongUsageOfUnsafe.go
Normal file
300
ql/test/experimental/Unsafe/WrongUsageOfUnsafe.go
Normal file
@@ -0,0 +1,300 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
func good0() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret without overflowing to secret:
|
||||
// NOTE: unsafe.Pointer(&harmless) == unsafe.Pointer(&harmless[0])
|
||||
var data = (*[8]byte)(unsafe.Pointer(&harmless)) // OK
|
||||
|
||||
fmt.Println(string((*data)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func goodIndexExpr() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret without overflowing to secret:
|
||||
// NOTE: unsafe.Pointer(&harmless) == unsafe.Pointer(&harmless[0])
|
||||
var data = (*[8]byte)(unsafe.Pointer(&harmless[0])) // OK
|
||||
|
||||
fmt.Println(string((*data)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func goodIndexExprDifferentTypes() {
|
||||
// A harmless piece of data:
|
||||
harmless := [12]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret without overflowing to secret:
|
||||
// Even tough `harmless` and `data` have types of different sizes,
|
||||
// `data` is made of 8 bytes starting from `harmless[3]`,
|
||||
// up until the end of `harmless` (from `harmless[3]` to
|
||||
// the end of `harmless` is 8 bytes),
|
||||
// which does not cross into `secret`.
|
||||
var data = (*[8]byte)(unsafe.Pointer(&harmless[3])) // OK
|
||||
|
||||
fmt.Println(string((*data)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func badIndexExpr() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret.
|
||||
// NOTE: unsafe.Pointer(&harmless) != unsafe.Pointer(&harmless[2])
|
||||
// Even tough harmless and leaking have the same size,
|
||||
// the new variable `leaking` will contain data starting from
|
||||
// the address of the 3rd element of the `harmless` array,
|
||||
// and continue for 8 bytes, going out of the boundaries of
|
||||
// `harmless` and crossing into the memory occupied by `secret`.
|
||||
var leaking = (*[8]byte)(unsafe.Pointer(&harmless[2])) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func good2() {
|
||||
// A harmless piece of data:
|
||||
harmless := uint(123)
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret:
|
||||
var data = (*int)(unsafe.Pointer(&harmless)) // TODO: is this really OK?
|
||||
|
||||
fmt.Println(*data)
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
|
||||
func bad0() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we get the pointer to the first byte of harmless)
|
||||
var leaking = (*[8 + 9]byte)(unsafe.Pointer(&harmless[0])) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
|
||||
func bad1() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless)
|
||||
var leaking = (*[8 + 9]byte)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
|
||||
type Harmless [8]byte
|
||||
|
||||
func bad2() {
|
||||
// A harmless piece of data:
|
||||
harmless := Harmless{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless)
|
||||
var leaking = (*[8 + 9]byte)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func bad3() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]string{"A", "A", "A", "A", "A", "A", "A", "A"}
|
||||
// Something secret:
|
||||
secret := [9]string{"s", "e", "n", "s", "i", "t", "i", "v", "e"}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless)
|
||||
var leaking = (*[8 + 9]string)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(*leaking)
|
||||
fmt.Println([17]string((*leaking)))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == "42" {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func bad4() {
|
||||
type harmlessType struct {
|
||||
Data [8]byte
|
||||
}
|
||||
// A harmless piece of data:
|
||||
harmless := harmlessType{
|
||||
Data: [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'},
|
||||
}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless)
|
||||
var leaking = (*[8 + 9]byte)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(string((*leaking)[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func bad5() {
|
||||
type harmlessType struct {
|
||||
Data [8]byte
|
||||
}
|
||||
// A harmless piece of data:
|
||||
harmless := harmlessType{
|
||||
Data: [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'},
|
||||
}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless)
|
||||
var leaking = (*[8 + 9]byte)(unsafe.Pointer(&harmless.Data)) // BAD
|
||||
|
||||
fmt.Println(string(leaking[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func bad6() {
|
||||
// A harmless piece of data:
|
||||
harmless := [8]byte{'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret:
|
||||
var leaking = buffer_request(unsafe.Pointer(&harmless)) // BAD (see inside buffer_request func)
|
||||
|
||||
fmt.Println((string)(leaking[:]))
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func buffer_request(req unsafe.Pointer) [8 + 9]byte {
|
||||
// The length of req is 8 bytes,
|
||||
// but we cast it to a longer array,
|
||||
// which means that when the resulting array
|
||||
// will be read, the read will also contain pieces of
|
||||
// data from `secret`.
|
||||
var buf [8 + 9]byte
|
||||
buf = *(*[8 + 9]byte)(req) // BAD (from above func)
|
||||
return buf
|
||||
}
|
||||
func bad7() {
|
||||
// A harmless piece of data:
|
||||
harmless := [1]int64{23}
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless);
|
||||
// the leaking array will not contain letters,
|
||||
// but integers representing bytes from `secret`.
|
||||
var leaking = (*[4]int64)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(*leaking)
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func bad8() {
|
||||
// A harmless piece of data:
|
||||
harmless := int8(123)
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless);
|
||||
// the leaking data will contain some bits from `secret`.
|
||||
var leaking = (*int64)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(*leaking)
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
func bad9() {
|
||||
// A harmless piece of data:
|
||||
harmless := int8(123)
|
||||
// Something secret:
|
||||
secret := [9]byte{'s', 'e', 'n', 's', 'i', 't', 'i', 'v', 'e'}
|
||||
|
||||
// Read before secret, overflowing into secret
|
||||
// (notice we read more than the length of harmless);
|
||||
// the leaking data will contain some bits from `secret`.
|
||||
var leaking = (*int)(unsafe.Pointer(&harmless)) // BAD
|
||||
|
||||
fmt.Println(*leaking)
|
||||
|
||||
// Avoid optimization:
|
||||
if secret[0] == 123 {
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
}
|
||||
1
ql/test/experimental/Unsafe/WrongUsageOfUnsafe.qlref
Normal file
1
ql/test/experimental/Unsafe/WrongUsageOfUnsafe.qlref
Normal file
@@ -0,0 +1 @@
|
||||
experimental/Unsafe/WrongUsageOfUnsafe.ql
|
||||
Reference in New Issue
Block a user