Debug School

rakesh kumar
rakesh kumar

Posted on

Memory Exhaustion, Memory Corruption, and Memory Leaks in Go: Can Hackers Exploit Them?

Meta description: Understand how memory exhaustion, memory corruption, and memory leaks can affect Go services, with simple real-world examples and practical protections.

Suggested slug: go-memory-exhaustion-corruption-leaks-security

Memory problems can give attackers opportunities, including in Go. However, running out of memory does not automatically let someone take control of your server. Different memory problems have different consequences.

The scenarios below are illustrative examples, not evidence of an attack on your application. The Go code demonstrates memory retention; it is not a complete server.

What Is the Difference?

Problem Simple meaning Possible attacker opportunity
Memory exhaustion The application runs out of available or permitted memory Make the service unavailable
Memory corruption Software incorrectly reads or writes memory Crash the service or, with some exploitable bugs, execute malicious code
Memory leak Unneeded memory remains retained Repeatedly trigger memory growth until the service fails

1. Memory Exhaustion: Filling Every Seat

A real-world analogy

Imagine a restaurant with 100 seats. Someone occupies every seat, leaving no room for genuine customers. The restaurant still exists, but it cannot serve new visitors.

Memory exhaustion works similarly: the application consumes the memory it needs to keep serving users.

Website scenario: too many PDF jobs

Your document service accepts uploads and extracts text from PDFs. Each extraction requires memory. If it accepts unlimited simultaneous jobs, repeated expensive requests can overload it.

Step What happens
1 Many documents arrive
2 The service starts too many extraction jobs together
3 Memory consumption grows beyond available resources
4 Workers fail or the operating system terminates processes
5 Genuine users cannot process their documents

Illustrative result:

Document processing unavailable. Please retry later.
Enter fullscreen mode Exit fullscreen mode

This can become a denial-of-service attack. It can also happen accidentally during a legitimate traffic spike. The behavior depends on the runtime, operating system, and configured limits.

Does the attacker control the server?

Not from exhaustion alone. The usual effect is disruption. Executing attacker-controlled code requires a separate exploitable weakness.

How to reduce the risk

  • Limit request and individual file sizes.
  • Bound simultaneous processing jobs.
  • Set a maximum queue length and expire old jobs.
  • Apply processing deadlines and worker memory limits.
  • Stream files rather than loading unlimited input into RAM.
  • Leave sufficient memory for the operating system and other services.

For example, allow two extraction workers and queue additional work within a fixed capacity. A limit on each worker must also fit the total host budget.

2. Memory Corruption: Using the Wrong Locker

A real-world analogy

Imagine a locker is emptied and assigned to a new person. An employee still uses its old ownership record and accesses the wrong person's belongings.

Software can have a similar problem when it uses a reference to memory after that memory was freed. This is called use-after-free, one form of invalid memory access. Other corruption bugs involve writing outside an allocated region.

Website scenario: a vulnerable native parser

A document service calls a native library to process an uploaded file. That library contains a memory-management bug. A specially crafted document reaches the vulnerable code path.

Step What happens
1 The service receives a crafted document
2 A vulnerable parser processes it
3 The parser incorrectly accesses memory
4 The worker may crash
5 If the bug is exploitable, an attacker may influence execution

Possible outcomes:

Outcome A: the parser worker crashes.
Outcome B: a particular exploitable bug permits malicious code execution.
Enter fullscreen mode Exit fullscreen mode

A crash does not prove code execution. Exploitability depends on the bug, build, protections, and environment. An attack also runs within the permissions of the compromised process unless another weakness allows further privilege escalation.

How does this apply to Go?

Ordinary safe Go code benefits from garbage collection and bounds checks. It does not normally let application code perform arbitrary pointer arithmetic or manually free objects.

However, Go applications can interact with:

  • Code using the unsafe package.
  • C libraries through cgo.
  • Native document, image, or other processing components.
  • Runtime or compiler implementation bugs.

Incorrect unsafe operations or a vulnerable native library can weaken the protections provided by ordinary Go code. Their presence alone does not prove a vulnerability.

How to reduce the risk

  • Patch the affected runtime and libraries.
  • Minimize unnecessary unsafe or native integrations.
  • Review security advisories for the exact deployed components.
  • Process untrusted files in isolated workers.
  • Give workers only the permissions and credentials they need.
  • Avoid unnecessary network access and sensitive filesystem mounts.

Isolation reduces potential damage; it does not repair the underlying parser bug.

3. Memory Leaks: Never Clearing Used Tables

A real-world analogy

Imagine restaurant staff never clear tables after customers finish. Over time, every table stays occupied by things nobody needs.

An application has a similar issue when it retains data that is no longer useful.

Go example: retaining every upload

var history [][]byte

func rememberUpload(data []byte) {
    history = append(history, data)
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

Code Meaning
[][]byte A slice containing byte slices
history A global variable that remains reachable
append(history, data) Keeps a reference to another uploaded byte slice

Go's garbage collector cannot reclaim objects that remain reachable. If history continues referencing every upload, those bytes stay retained.

This is often called a logical memory leak: the data is still accessible to the program but is no longer needed. If called by concurrent request handlers, this example also needs synchronization; it is intentionally simplified.

Website scenario: repeated calls accelerate retention

An endpoint unnecessarily remembers every document. Repeated requests make the retained data grow faster.

Illustrative retained payload sizes, assuming separate 1 MiB allocations per upload:

Uploads retained Approximate payload memory
10 10 MiB
100 100 MiB
1,000 1,000 MiB

Actual process memory also includes slice metadata, allocation overhead, runtime memory, and other work. Growth can eventually cause exhaustion and service failure.

Better example: retain bounded metadata

If you only need recent upload names, do not retain full document contents:

package main

import "fmt"

const maxHistory = 3

func rememberName(history []string, name string) []string {
    history = append(history, name)

    if len(history) > maxHistory {
        // Copy the retained names into a new slice.
        history = append([]string(nil), history[len(history)-maxHistory:]...)
    }

    return history
}

func main() {
    var history []string

    for _, name := range []string{"a.pdf", "b.pdf", "c.pdf", "d.pdf"} {
        history = rememberName(history, name)
    }

    fmt.Println(history)
}
Enter fullscreen mode Exit fullscreen mode

Expected output:

[b.pdf c.pdf d.pdf]
Enter fullscreen mode Exit fullscreen mode

This keeps only three names. A real service must also bound name length and protect shared state from concurrent access. Prefer durable storage when records need to be preserved.

How to reduce the risk

  • Bound caches and retained history.
  • Set expiry times and actually remove expired entries.
  • Release references to large objects when finished.
  • Close resources such as files and response bodies appropriately.
  • Monitor memory over time and inspect heap profiles when growth is unexplained.
  • Limit requests to endpoints that perform expensive or state-growing work.

Clearing references makes objects eligible for garbage collection; it does not guarantee immediate return of memory to the operating system.

What Could Happen to a Real Business?

Service Example weakness Business impact
Contract upload Unlimited parsing jobs Users cannot analyze contracts
Booking API Unbounded retained request history Booking requests slow down or fail
Image processor Exploitable native-library bug Worker compromise; accessible data may be at risk
Shared host Total worker memory exceeds host capacity Other applications may also be interrupted

A single bad file should ideally fail one isolated job instead of taking down every website on the server.

Does Switching to Go Solve Everything?

No. Go helps prevent many common invalid-memory operations in ordinary application code. It does not automatically prevent excessive allocations, unbounded caches, unsafe integrations, application authorization bugs, or vulnerable dependencies.

Use multiple layers together:

  1. Patched runtimes and dependencies.
  2. Input-size, time, concurrency, and queue limits.
  3. Streaming and bounded data retention.
  4. Isolated workers with resource limits.
  5. Non-root users and restricted credentials.
  6. Monitoring and controlled recovery.
  7. Staging tests for malformed input and overload.

Frequently Asked Questions

Can a hacker take control just because memory is full?

Usually not. Exhaustion generally threatens availability. Code execution requires an exploitable bug or another weakness.

Does garbage collection prevent every leak?

No. If your program still references unnecessary data, garbage collection normally keeps it alive.

Does try-catch or error recovery stop all memory attacks?

No. Normal error handling cannot reliably recover from a process killed by the operating system or all forms of native corruption.

Does a worker restart fix a memory leak?

It clears that process's memory, but the leak can recur. Fix the retention problem and use restarts as a controlled operational measure.

Which problem is most concerning?

All matter. Exhaustion and leaks can cause serious outages. Exploitable corruption can additionally threaten data confidentiality and process control.

Further Reading

Top comments (0)