uutils / uutils/coreutils

base64: stdin buffering causes memory exhaustion and performance degradation

Open
#9,213 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

U - base64
Dominant language
Rust
Stars
24.1k
Forks
2k
Avg merge
1d 5h
Merged PRs (30d)
365

Description

Running base64 --decode on stdin loads the entire input into memory before processing begins. This blocks on EOF and exhausts RAM with large streams.

Reproduction

Create a 500MB base64-encoded file:

dd if=/dev/zero bs=1M count=100 2>/dev/null | base64 > /tmp/large_base64.txt
# Creates ~133MB file (base64 expansion)

Test uutils:

head -c 500000000 /tmp/large_base64.txt | /usr/bin/time -l ./target/debug/coreutils base64 --decode > /dev/null 2>&1

Test GNU base64:

head -c 500000000 /tmp/large_base64.txt | /usr/bin/time -l base64 --decode > /dev/null 2>&1

Test Results:

Tool Peak Memory Memory Used
uutils base64 341,722,816 bytes ~325 MB
GNU base64 1,032,896 bytes ~1 MB
Difference 331x more uutils exhausts RAM

Result: uutils uses ~331x more memory (325 MB vs 1 MB) on the same 500MB stream.

Root Cause

https://github.com/uutils/coreutils/blob/6cf5b06d8be0d8b6b4882dcdd1e7838376c6262d/src/uu/base32/src/base_common.rs#L166-L170

read_to_end() waits for EOF before returning. Files larger than RAM cause memory exhaustion.

What GNU does

GNU coreutils (basenc.c) uses a chunk-based loop:

No buffering, no EOF blocking, handles infinite streams.

References

  1. GitHub Issue #8574: base64 almost 8x slower than GNU for large files

  2. Hacker News Discussion: Show HN: ut - Rust based CLI utilities

  3. GNU coreutils Release Notes: Version 9.5 (Mar 28, 2024)

    • "base32 and base64 no longer require padding when decoding"
    • Indicates modern GNU behavior moving toward STANDARD_NO_PAD for stdin

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/uu/base32/src/base_common.rs at lines 166-170, where read_to_end() is identified as the source of whole-input buffering. Compare the reported GNU basenc chunk-based behavior, then run the reproduction commands against uutils and GNU base64. Done means decoding large stdin streams without waiting for EOF or exhausting memory.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli, performance
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.