Skip to content

Go: add memoryAllocationDos query #12663

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions go/ql/src/experimental/CWE-20/MemoryAllocationDos.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>
Memory allocation size from user-controlled data can allow an attacker to uses a lot of
server memory. This can result the server is unavailable or crash due to failure to allocate huge memory.
</p>
</overview>


<references>
<li>OWASP: <a href="https://owasp.org/www-community/attacks/Denial_of_Service"> Denial_of_Service</a>.</li>
</references>
</qhelp>
88 changes: 88 additions & 0 deletions go/ql/src/experimental/CWE-20/MemoryAllocationDos.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @name User-controlled size of a memory allocation operation
* @description Allowing a user to influence the size of a memory allocation could lead to denial of service attacks
* @kind path-problem
* @problem.severity error
* @id go/allow-memory-size-injection
* @tag security
* experimental
* external/cwe/cwe-023
*/

import go
import semmle.go.security.AllocationSizeOverflow
import semmle.go.frameworks.Stdlib
import DataFlow::PathGraph

abstract class Source extends DataFlow::Node { }

abstract class Sanitizer extends DataFlow::Node { }

private SsaWithFields getAComparedVar() {
exists(RelationalComparisonExpr e |
not e.getLesserOperand().isConst() or e.getLesserOperand().getIntValue() != 0
|
result.getAUse().asExpr() = e.getGreaterOperand()
)
}

class CompSanitizer extends Sanitizer {
CompSanitizer() { this = getAComparedVar().similar().getAUse() }
}

class FieldReadSanitizer extends Sanitizer {
FieldReadSanitizer() {
exists(DataFlow::FieldReadNode f |
f.asExpr() = any(RelationalComparisonExpr e).getGreaterOperand()
|
this.asInstruction() = f.asInstruction().getASuccessor+()
)
}
}

private predicate compCheckGuard(DataFlow::Node g, Expr e, boolean outcome) {
e = g.(DataFlow::RelationalComparisonNode).getAnOperand().asExpr() and
outcome = [true, false]
}

class CompSanitizerGuard extends Sanitizer {
CompSanitizerGuard() { this = DataFlow::BarrierGuard<compCheckGuard/3>::getABarrierNode() }
}

string macaronContextPath() { result = package("gopkg.in/macaron", "") }

class UntrustedFlowAsSource extends Source instanceof UntrustedFlowSource { }

class MacaronInputSource extends Source, DataFlow::MethodCallNode {
MacaronInputSource() {
this.getTarget().hasQualifiedName(macaronContextPath(), "Context", ["QueryInt", "QueryInt64"])
}
}

class Configuration extends TaintTracking::Configuration {
Configuration() { this = "MemoryAllocationDos" }

override predicate isSink(DataFlow::Node sink) {
sink instanceof AllocationSizeOverflow::AllocationSize
}

override predicate isSource(DataFlow::Node source) { source instanceof Source }

override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(DataFlow::CallNode call |
node2 = call.getResult(0) and
call.getTarget() instanceof IntegerParser::Range and
call.getArgument(0) = node1
)
}

override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
}
}

from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Untrusted memory allocation size depends on a $@.",
source.getNode(), "user-provided value"
33 changes: 33 additions & 0 deletions go/ql/src/experimental/CWE-20/MemoryAllocationDosBad.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package server

import (
"net/http"
"strconv"
)

type testStruct struct {
PageSize int
}


func serve_bad() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {

c, _ := strconv.Atoi(r.Form.Get("count"))
_ = make([]int, c)

d, _ := strconv.Atoi(r.Form.Get("count2"))
if(d <= 0){
d = 20
}
_ = make([]int, d)

e, _ := strconv.Atoi(r.Form.Get("count3"))
if(e > 0){
// some extra operation
}
_ = make([]int, e)

})
http.ListenAndServe(":80", nil)
}
34 changes: 34 additions & 0 deletions go/ql/src/experimental/CWE-20/MemoryAllocationDosGood.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package server

import (
"net/http"
"strconv"
)

type testStruct struct {
PageSize int
}


func getInt(r *http.Request) int {
ret, _ := strconv.Atoi(r.Form.Get("count3"))
return ret
}

func serve_good() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {

c, _ := strconv.Atoi(r.Form.Get("count"))
if c > 200 {
c = 200
}
_ = make([]int, c)

f, _ := strconv.Atoi(r.Form.Get("count4"))
if(f < 20){
_ = make([]int, f)
}

})
http.ListenAndServe(":80", nil)
}
16 changes: 16 additions & 0 deletions go/ql/test/experimental/CWE-20/MemoryAllocationDos.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
edges
| MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | MemoryAllocationDosBad.go:17:19:17:19 | c |
| MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | MemoryAllocationDosBad.go:23:19:23:19 | d |
| MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | MemoryAllocationDosBad.go:29:19:29:19 | e |
nodes
| MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | semmle.label | selection of Form |
| MemoryAllocationDosBad.go:17:19:17:19 | c | semmle.label | c |
| MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | semmle.label | selection of Form |
| MemoryAllocationDosBad.go:23:19:23:19 | d | semmle.label | d |
| MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | semmle.label | selection of Form |
| MemoryAllocationDosBad.go:29:19:29:19 | e | semmle.label | e |
subpaths
#select
| MemoryAllocationDosBad.go:17:19:17:19 | c | MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | MemoryAllocationDosBad.go:17:19:17:19 | c | Untrusted memory allocation size depends on a $@. | MemoryAllocationDosBad.go:16:24:16:29 | selection of Form | user-provided value |
| MemoryAllocationDosBad.go:23:19:23:19 | d | MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | MemoryAllocationDosBad.go:23:19:23:19 | d | Untrusted memory allocation size depends on a $@. | MemoryAllocationDosBad.go:19:24:19:29 | selection of Form | user-provided value |
| MemoryAllocationDosBad.go:29:19:29:19 | e | MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | MemoryAllocationDosBad.go:29:19:29:19 | e | Untrusted memory allocation size depends on a $@. | MemoryAllocationDosBad.go:25:24:25:29 | selection of Form | user-provided value |
1 change: 1 addition & 0 deletions go/ql/test/experimental/CWE-20/MemoryAllocationDos.qlref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
experimental/CWE-20/MemoryAllocationDos.ql
33 changes: 33 additions & 0 deletions go/ql/test/experimental/CWE-20/MemoryAllocationDosBad.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package server

import (
"net/http"
"strconv"
)

type testStruct struct {
PageSize int
}


func serve_bad() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {

c, _ := strconv.Atoi(r.Form.Get("count"))
_ = make([]int, c)

d, _ := strconv.Atoi(r.Form.Get("count2"))
if(d <= 0){
d = 20
}
_ = make([]int, d)

e, _ := strconv.Atoi(r.Form.Get("count3"))
if(e > 0){
// some extra operation
}
_ = make([]int, e)

})
http.ListenAndServe(":80", nil)
}
29 changes: 29 additions & 0 deletions go/ql/test/experimental/CWE-20/MemoryAllocationDosGood.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package server

import (
"net/http"
"strconv"
)

type testStruct struct {
PageSize int
}


func getInt(r *http.Request) int {
ret, _ := strconv.Atoi(r.Form.Get("count3"))
return ret
}

func serve_good() {
http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) {

c, _ := strconv.Atoi(r.Form.Get("count"))
if c > 200 {
c = 200
}
_ = make([]int, c)

})
http.ListenAndServe(":80", nil)
}