Merge pull request #235 from gagliardetto/bad-unsafe

Query to find wrong uses of package "unsafe"
This commit is contained in:
Max Schaefer
2020-07-03 09:36:10 +01:00
committed by GitHub
7 changed files with 661 additions and 0 deletions

View 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")
}
}

View 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")
}
}

View 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>

View 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

View 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 |

View 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")
}
}

View File

@@ -0,0 +1 @@
experimental/Unsafe/WrongUsageOfUnsafe.ql