gc-uaf-blindspot.dhover×73all
#!/usr/bin/env dub
/+ dub.sdl:
    name "gc_uaf_blindspot"
    platforms "linux"
    dflags "-g"
    dflags "-fsanitize=address" platform="ldc"
    targetPath "build"
+/
/**
 * The AddressSanitizer GC blind spot: a use-after-free inside GC-managed
 * memory is invisible to ASan, while the identical bug on `malloc`/`free`
 * memory is caught — demonstrated as a self-verifying contrast pair.
 *
 * Two child-process demonstrations (the parent re-execs itself and asserts on
 * each child's exit code and stderr report):
 *
 *   1. `GC.malloc` -> `GC.free` -> read. The D GC allocates its pools with
 *      `mmap` (`core/internal/gc/os.d`, `os_mem_map`) and recycles memory
 *      internally, so freed GC memory never passes through ASan's intercepted
 *      `free` and its shadow is never poisoned: the child reads a garbage
 *      value and exits 0 — ASan reports nothing. The blind spot.
 *   2. `core.stdc.stdlib.malloc` -> `free` -> read. The C allocator *is*
 *      intercepted (quarantine + shadow poisoning), so the same read dies
 *      with `heap-use-after-free`, a symbolized `file:line`, and exit 1.
 *
 * Companion to docs/research/sanitizers/d-toolchain.md
 *   § "The GC blind spot: ASan cannot see GC pools".
 *
 * Run with: dub run --single gc-uaf-blindspot.d
 *
 * Environment recorded: Linux 6.18.26, AMD Ryzen 9 7940HX (Zen 4), LDC 1.41.0
 * (LLVM 18.1.8). nixpkgs LDC ships no compiler-rt, so the ASan runtime is GCC
 * 15.2.0's `libasan.so.8` via LDC's `linker-gcc.cpp` fallback (`-fsanitize=
 * address` handed to the C-compiler link driver); it self-symbolizes without
 * `llvm-symbolizer`.
 *
 * Portability: builds without instrumentation (DMD has no `-fsanitize`; the
 * flag is gated `platform="ldc"`), in which case — detected at compile time
 * via the `LDC_AddressSanitizer` predefined version — the probe prints a
 * `SKIP:` line and exits 0 so CI stays green on any host.
 */
module 
(module) gc_uaf_blindspot

The AddressSanitizer GC blind spot: a use-after-free inside GC-managed memory is invisible to ASan, while the identical bug on malloc/free memory is caught — demonstrated as a self-verifying contrast pair.

Two child-process demonstrations (the parent re-execs itself and asserts on each child's exit code and stderr report):

  1. GC.malloc -> GC.free -> read. The D GC allocates its pools with mmap (core/internal/gc/os.d, os_mem_map) and recycles memory internally, so freed GC memory never passes through ASan's intercepted free and its shadow is never poisoned: the child reads a garbage value and exits 0 — ASan reports nothing. The blind spot.

  2. core.stdc.stdlib.malloc -> free -> read. The C allocator is intercepted (quarantine + shadow poisoning), so the same read dies with heap-use-after-free, a symbolized file:line, and exit 1.

Companion to docs/research/sanitizers/d-toolchain.md § "The GC blind spot: ASan cannot see GC pools".

Run with: dub run --single gc-uaf-blindspot.d

Environment recorded: Linux 6.18.26, AMD Ryzen 9 7940HX (Zen 4), LDC 1.41.0 (LLVM 18.1.8). nixpkgs LDC ships no compiler-rt, so the ASan runtime is GCC 15.2.0's libasan.so.8 via LDC's linker-gcc.cpp fallback (-fsanitize= address handed to the C-compiler link driver); it self-symbolizes without llvm-symbolizer.

Portability

builds without instrumentation (DMD has no -fsanitize; the flag is gated platform="ldc"), in which case — detected at compile time via the LDC_AddressSanitizer predefined version — the probe prints a SKIP: line and exits 0 so CI stays green on any host.

gc_uaf_blindspot
;
import
(package) std
std
.
(module) std.stdio
Category Symbols
File handles _popen File isFileHandle openNetwork stderr stdin stdout
Reading chunks lines readf readfln readln
Writing toFile write writef writefln writeln
Misc KeepTerminator LockType StdioException

Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio is publically imported when importing std.stdio.

There are three layers of I/O:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
stdio
:
(alias template) gc_uaf_blindspot.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

Equivalent to writef(fmt, args, '\n').

writefln
,
(alias template) gc_uaf_blindspot.writeln = std.stdio.writeln(T...)(T args)

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
;
version (
LDC_AddressSanitizer
LDC_AddressSanitizer
)
private enum instrumented = true; else private enum
(constant) bool gc_uaf_blindspot.instrumented = false
instrumented
= false;
/// Name of the env var that selects the faulty child leg. private enum
(constant) string gc_uaf_blindspot.childVar = "GC_UAF_BLINDSPOT_CHILD"

Name of the env var that selects the faulty child leg.

childVar
= "GC_UAF_BLINDSPOT_CHILD";
/// Child leg 1: use-after-free entirely inside GC-managed memory. /// ASan never sees the GC's mmap'd pools, so this runs to completion. private int
int gc_uaf_blindspot.runGcLeg()

Child leg 1: use-after-free entirely inside GC-managed memory. ASan never sees the GC's mmap'd pools, so this runs to completion.

runGcLeg
()
{ import
(package) core
core
.
(module) core.memory

This module provides an interface to the garbage collector used by applications written in the D programming language. It allows the garbage collector in the runtime to be swapped without affecting binary compatibility of applications.

Using this module is not necessary in typical D code. It is mostly useful when doing low-level memory management.

Notes to users

  1. The GC is a conservative mark-and-sweep collector. It only runs a collection cycle when an allocation is requested of it, never otherwise. Hence, if the program is not doing allocations, there will be no GC collection pauses. The pauses occur because all threads the GC knows about are halted so the threads' stacks and registers can be scanned for references to GC allocated data.

  2. The GC does not know about threads that were created by directly calling the OS/C runtime thread creation APIs and D threads that were detached from the D runtime after creation. Such threads will not be paused for a GC collection, and the GC might not detect references to GC allocated data held by them. This can cause memory corruption. There are several ways to resolve this issue:

    1. Do not hold references to GC allocated data in such threads.

    2. Register/unregister such data with calls to addRoot/removeRoot and addRange/removeRange.

    3. Maintain another reference to that same data in another thread that the GC does know about.

    4. Disable GC collection cycles while that thread is active with disable/enable.

    5. Register the thread with the GC using thread_attachThis/thread_detachThis.

Notes to implementors

  • On POSIX systems, the signals SIGRTMIN and SIGRTMIN + 1 are reserved by this module for use in the garbage collector implementation. Typically, they will be used to stop and resume other threads when performing a collection, but an implementation may choose not to use this mechanism (or not stop the world at all, in the case of concurrent garbage collectors).

  • Registers, the stack, and any other memory locations added through the GC.addRange`` function are always scanned conservatively. This means that even if a variable is e.g. of type float, it will still be scanned for possible GC pointers. And, if the word-interpreted representation of the variable matches a GC-managed memory block's address, that memory block is considered live.

  • Implementations are free to scan the non-root heap in a precise manner, so that fields of types like float will not be considered relevant when scanning the heap. Thus, casting a GC pointer to an integral type (e.g. size_t) and storing it in a field of that type inside the GC heap may mean that it will not be recognized if the memory block was allocated with precise type info or with the GC.BlkAttr.NO_SCAN`` attribute.

  • Destructors will always be executed while other threads are active; that is, an implementation that stops the world must not execute destructors until the world has been resumed.

  • A destructor of an object must not access object references within the object. This means that an implementation is free to optimize based on this rule.

  • An implementation is free to perform heap compaction and copying so long as no valid GC pointers are invalidated in the process. However, memory allocated with GC.BlkAttr.NO_MOVE`` must not be moved/copied.

  • Implementations must support interior pointers. That is, if the only reference to a GC-managed memory block points into the middle of the block rather than the beginning (for example), the GC must consider the memory block live. The exception to this rule is when a memory block is allocated with the GC.BlkAttr.NO_INTERIOR`` attribute; it is the user's responsibility to make sure such memory blocks have a proper pointer to them when they should be considered live.

  • It is acceptable for an implementation to store bit flags into pointer values and GC-managed memory blocks, so long as such a trick is not visible to the application. In practice, this means that only a stop-the-world collector can do this.

  • Implementations are free to assume that GC pointers are only stored on word boundaries. Unaligned pointers may be ignored entirely.

  • Implementations are free to run collections at any point. It is, however, recommendable to only do so when an allocation attempt happens and there is insufficient memory available.

Source

core/memory.d

@copyrightCopyright Sean Kelly 2005 - 2015.@licenseBoost License 1.0@authorsSean Kelly, Alex Rønne Petersen
memory
:
(struct) core.memory.GC

This struct encapsulates all garbage collection functionality for the D programming language.

GC
;
int*
(local variable) int* p
p
= cast(int*)
(struct) core.memory.GC

This struct encapsulates all garbage collection functionality for the D programming language.

GC
.
void* core.memory.GC.malloc(ulong sz, uint ba = 0u, scope const(object.TypeInfo) ti = null) pure nothrow

Requests an aligned block of managed memory from the garbage collector. This memory may be deleted at will with a call to free, or it may be discarded and cleaned up automatically during a collection run. If allocation fails, this function will call onOutOfMemory which is expected to throw an OutOfMemoryError.

@paramsz The desired allocation size in bytes.@paramba A bitmask of the attributes to set on this block.@paramti TypeInfo to describe the memory. The GC might use this information to improve scanning for pointers or to call finalizers.@returns

A reference to the allocated memory or null if insufficient memory is available.

Allocated memory

@throwsOutOfMemoryError on allocation failure.
malloc
(64);
(local variable) int* p
p
[0] = 42;
(struct) core.memory.GC

This struct encapsulates all garbage collection functionality for the D programming language.

GC
.
void core.memory.GC.free(void* p) pure nothrow @nogc

Deallocates the memory referenced by p. If p is null, no action occurs. If p references memory not originally allocated by this garbage collector, if p points to the interior of a memory block, or if this method is called from a finalizer, no action will be taken. The block will not be finalized regardless of whether the FINALIZE attribute is set. If finalization is desired, call destroy prior to GC.free``.

@paramp A pointer to the root of a valid memory block or to null.
free
(
(local variable) int* p
p
);
// Deliberate read-after-free of GC memory (the value is garbage).
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("gc leg: read-after-GC.free = %s (not flagged)",
(local variable) int* p
p
[0]);
return 0; } /// Child leg 2: the identical bug on the intercepted C allocator. /// ASan poisons freed memory, so this read aborts with a report. private int
int gc_uaf_blindspot.runMallocLeg()

Child leg 2: the identical bug on the intercepted C allocator. ASan poisons freed memory, so this read aborts with a report.

runMallocLeg
()
{ import
(package) core
core
.
(package) core.stdc
stdc
.
(module) core.stdc.stdlib

D header file for C99.

pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h

Source

core/stdc/stdlib.d

@copyrightCopyright Sean Kelly 2005 - 2014.@licenseDistributed under the Boost Software License 1.0. (See accompanying file LICENSE)@authorsSean Kelly@standardsISO/IEC 9899:1999 (E)
stdlib
:
(alias) free = void core.stdc.stdlib.free(void* ptr) nothrow @nogc
free
,
(alias) malloc = void* core.stdc.stdlib.malloc(ulong size) nothrow @nogc
malloc
;
int*
(local variable) int* p
p
= cast(int*)
void* core.stdc.stdlib.malloc(ulong size) nothrow @nogc
malloc
(64);
(local variable) int* p
p
[0] = 42;
void core.stdc.stdlib.free(void* ptr) nothrow @nogc
free
(
(local variable) int* p
p
);
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("malloc leg: read-after-free = %s (should never print)",
(local variable) int* p
p
[0]);
return 0; } /// Re-execs this binary with `childVar=leg`, captures combined output, and /// returns (exitCode, output). private auto
std.typecons.Tuple!(int, "status", string, "output") gc_uaf_blindspot.runChild(string leg) @safe

Re-execs this binary with childVar=leg``, captures combined output, and returns (exitCode, output).

runChild
(
(alias) object.string = string
string
(parameter) string leg
leg
)
{ import
(package) std
std
.
(module) std.process

Functions for starting and interacting with other processes, and for working with the current process' execution environment.

Process handling

  • `spawnProcess` spawns a new `process`, optionally assigning it an
        

    arbitrary set of standard input, output, and error streams. The function returns immediately, leaving the child process to execute in parallel with its parent. All other functions in this module that spawn processes are built around spawnProcess.

  • `wait` makes the parent `process` wait for a child `process` to
        

    terminate. In general one should always do this, to avoid child processes becoming "zombies" when the parent process exits. Scope guards are perfect for this – see the spawnProcess documentation for examples. tryWait is similar to wait, but does not block if the process has not yet terminated.

  • `pipeProcess` also spawns a child `process` which runs
        

    in parallel with its parent. However, instead of taking arbitrary streams, it automatically creates a set of pipes that allow the parent to communicate with the child through the child's standard input, output, and/or error streams. This function corresponds roughly to C's popen function.

  • `execute` starts a new `process` and waits for it
        

    to complete before returning. Additionally, it captures the process' standard output and error streams and returns the output of these as a string.

  • `spawnShell`, `pipeShell` and `executeShell` work like
        

    spawnProcess, pipeProcess and execute, respectively, except that they take a single command string and run it through the current user's default command interpreter. executeShell corresponds roughly to C's system function.

  • `kill` attempts to terminate a running `process`.
    
    

The following table compactly summarises the different process creation functions and how they relate to each other:

Runs program directly
Runs shell command
Low-level process creation
spawnProcess
spawnShell
Automatic input/output redirection using pipes
pipeProcess
pipeShell
Execute and wait for completion, collect output
execute
executeShell

Other functionality

  • `pipe` is used to create unidirectional pipes.
    
  • `environment` is an interface through which the current `process`'
        

    environment variables can be read and manipulated.

  • `escapeShellCommand` and `escapeShellFileName` are useful
        

    for constructing shell command lines in a portable way.

Source

std/process.d

Note

Most of the functionality in this module is not available on iOS, tvOS and watchOS. The only functions available on those platforms are: environment, thisProcessID and thisThreadID.

@authorsLars Tandle Kyllingstad, Steven Schveighoffer, Vladimir Panteleev@copyrightCopyright (c) 2013, the authors. All rights reserved.@licenseBoost License 1.0.
process
:
(class) std.process.environment

Manipulates environment variables using an associative-array-like interface.

This class contains only static methods, and cannot be instantiated. See below for examples of use.

environment
,
(alias) escapeShellCommand = string std.process.escapeShellCommand(const(char[])[] args...) pure @safe

Escapes an argv-style argument array to be used with $(LREF spawnShell), $(LREF pipeShell) or $(LREF executeShell).

string url = "http://dlang.org/"; executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));

Concatenate multiple escapeShellCommand and $(LREF escapeShellFileName) results to use shell redirection or piping operators.

executeShell( escapeShellCommand("curl", "http://dlang.org/download.html") ~ "|" ~ escapeShellCommand("grep", "-o", http://\S*\.zip) ~ ">" ~ escapeShellFileName("D download links.txt"));

Throws: $(OBJECTREF Exception) if any part of the command line contains unescapable characters (NUL on all platforms, as well as CR and LF on Windows).

escapeShellCommand
,
(alias) executeShell = std.typecons.Tuple!(int, "status", string, "output") std.process.executeShell(scope const(char)[] command, const(string[string]) env = cast(const(string[string]))null, std.process.Config config = Config(Flags.none, null, null), ulong maxOutput = 18446744073709551615LU, scope const(char)[] workDir = null, string shellPath = nativeShell()) @safe
executeShell
;
// detect_leaks=0: keep the asserted exit codes purely about the // demonstrated defect (LSan's exit-at-process-end would add exit 23). const
(local variable) const(string[string]) env
env
= [
(constant) string gc_uaf_blindspot.childVar = "GC_UAF_BLINDSPOT_CHILD"

Name of the env var that selects the faulty child leg.

childVar
:
(parameter) string leg
leg
,
"ASAN_OPTIONS": "detect_leaks=0", ]; import
(package) std
std
.
(module) std.file

Utilities for manipulating files and scanning directories. Functions in this module handle files as a unit, e.g., read or write one file at a time. For opening files and manipulating them via handles refer to module std.stdio.

Category Functions
General exists isDir isFile isSymlink rename thisExePath
Directories chdir dirEntries getcwd mkdir mkdirRecurse rmdir rmdirRecurse tempDir
Files append copy read readText remove slurp write
Symlinks symlink readLink
Attributes attrIsDir attrIsFile attrIsSymlink getAttributes getLinkAttributes getSize setAttributes
Timestamp getTimes getTimesWin setTimes timeLastModified timeLastAccessed timeStatusChanged
Other DirEntry FileException PreserveAttributes SpanMode getAvailableDiskSpace

Source

std/file.d

@copyrightCopyright The D Language Foundation 2007 - 2011.@seeThe official tutorial for an introduction to working with files in D, module std.stdio for opening files and manipulating them via handles, and module std.path for manipulating path strings.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Jonathan M Davis
file
:
(alias) thisExePath = string std.file.thisExePath() @trusted

Returns the full path of the current executable.

Returns: The path of the executable as a string.

Throws: $(REF1 Exception, object)

thisExePath
;
return
std.typecons.Tuple!(int, "status", string, "output") std.process.executeShell(scope const(char)[] command, const(string[string]) env = cast(const(string[string]))null, std.process.Config config = Config(Flags.none, null, null), ulong maxOutput = 18446744073709551615LU, scope const(char)[] workDir = null, string shellPath = nativeShell()) @safe
executeShell
(
string std.process.escapeShellCommand(const(char[])[] args...) pure @safe

Escapes an argv-style argument array to be used with spawnShell, pipeShell or executeShell.

string url = "http://dlang.org/";
executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));

Concatenate multiple escapeShellCommand and escapeShellFileName results to use shell redirection or piping operators.

executeShell(
    escapeShellCommand("curl", "http://dlang.org/download.html") ~
    "|" ~
    escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~
    ">" ~
    escapeShellFileName("D download links.txt"));
@throwsException if any part of the command line contains unescapable characters (NUL on all platforms, as well as CR and LF on Windows).
escapeShellCommand
(
string std.file.thisExePath() @trusted

Returns the full path of the current executable.

Examples

import std.path : isAbsolute;
auto path = thisExePath();

assert(path.exists);
assert(path.isAbsolute);
assert(path.isFile);
@returnsThe path of the executable as a string.@throwsException
thisExePath
) ~ " 2>&1",
(local variable) const(string[string]) env
env
);
} version (
linux
linux
) private int
int gc_uaf_blindspot.run()
run
()
{ import
(package) std
std
.
(package) std.algorithm
algorithm
.
(module) std.algorithm.searching

This is a submodule of std.algorithm. It contains generic searching algorithms.

Function Name Description
all all!"a > 0"([1, 2, 3, 4]) returns true because all elements are positive
any any!"a > 0"([1, 2, -3, -4]) returns true because at least one element is positive
balancedParens balancedParens("((1 + 1) / 2)", '(', ')') returns true because the string has balanced parentheses.
boyerMooreFinder find("hello world", boyerMooreFinder("or")) returns "orld" using the Boyer-Moore algorithm.
canFind canFind("hello world", "or") returns true.
count Counts all elements or elements matching a predicate, specific element or sub-range.

count([1, 2, 1]) returns 3, count([1, 2, 1], 1) returns 2 and count!"a < 0"([1, -3, 0]) returns 1. | | countUntil | countUntil(a, b) returns the number of steps taken in a to reach b; for example, countUntil("hello!", "o") returns 4. | | commonPrefix | commonPrefix("parakeet", "parachute") returns "para". | | endsWith | endsWith("rocks", "ks") returns true. | | extrema | extrema([2, 1, 3, 5, 4]) returns [1, 5]. | | find | find("hello world", "or") returns "orld" using linear search. (For binary search refer to SortedRange.) | | findAdjacent | findAdjacent([1, 2, 3, 3, 4]) returns the subrange starting with two equal adjacent elements, i.e. [3, 3, 4]. | | findAmong | findAmong("abcd", "qcx") returns "cd" because 'c' is among "qcx". | | findSkip | If a = "abcde", then findSkip(a, "x") returns false and leaves a unchanged, whereas findSkip(a, "c") advances a to "de" and returns true. | | findSplit | findSplit("abcdefg", "de") returns a tuple of three ranges "abc", "de", and "fg". | | findSplitAfter | findSplitAfter("abcdefg", "de") returns a tuple of two ranges "abcde" and "fg". | | findSplitBefore | findSplitBefore("abcdefg", "de") returns a tuple of two ranges "abc" and "defg". | | minCount | minCount([2, 1, 1, 4, 1]) returns tuple(1, 3). | | maxCount | maxCount([2, 4, 1, 4, 1]) returns tuple(4, 2). | | minElement | Selects the minimal element of a range. minElement([3, 4, 1, 2]) returns 1. | | maxElement | Selects the maximal element of a range. maxElement([3, 4, 1, 2]) returns 4. | | minIndex | Index of the minimal element of a range. minIndex([3, 4, 1, 2]) returns 2. | | maxIndex | Index of the maximal element of a range. maxIndex([3, 4, 1, 2]) returns 1. | | minPos | minPos([2, 3, 1, 3, 4, 1]) returns the subrange [1, 3, 4, 1], i.e., positions the range at the first occurrence of its minimal element. | | maxPos | maxPos([2, 3, 1, 3, 4, 1]) returns the subrange [4, 1], i.e., positions the range at the first occurrence of its maximal element. | | skipOver | Assume a = "blah". Then skipOver(a, "bi") leaves a unchanged and returns false, whereas skipOver(a, "bl") advances a to refer to "ah" and returns true. | | startsWith | startsWith("hello, world", "hello") returns true. | | until | Lazily iterates a range until a specific value is found. |

Source

std/algorithm/searching.d

@copyrightAndrei Alexandrescu 2008-.@licenseBoost License 1.0.@authorsAndrei Alexandrescu
searching
:
(alias template) canFind = std.algorithm.searching.canFind(alias pred = "a == b")

Convenience function. Like find, but only returns whether or not the search was successful.

For more information about pred see $(LREF find).

See_Also: $(REF among, std,algorithm,comparison) for checking a value against multiple arguments.

canFind
;
import
(package) std
std
.
(module) std.process

Functions for starting and interacting with other processes, and for working with the current process' execution environment.

Process handling

  • `spawnProcess` spawns a new `process`, optionally assigning it an
        

    arbitrary set of standard input, output, and error streams. The function returns immediately, leaving the child process to execute in parallel with its parent. All other functions in this module that spawn processes are built around spawnProcess.

  • `wait` makes the parent `process` wait for a child `process` to
        

    terminate. In general one should always do this, to avoid child processes becoming "zombies" when the parent process exits. Scope guards are perfect for this – see the spawnProcess documentation for examples. tryWait is similar to wait, but does not block if the process has not yet terminated.

  • `pipeProcess` also spawns a child `process` which runs
        

    in parallel with its parent. However, instead of taking arbitrary streams, it automatically creates a set of pipes that allow the parent to communicate with the child through the child's standard input, output, and/or error streams. This function corresponds roughly to C's popen function.

  • `execute` starts a new `process` and waits for it
        

    to complete before returning. Additionally, it captures the process' standard output and error streams and returns the output of these as a string.

  • `spawnShell`, `pipeShell` and `executeShell` work like
        

    spawnProcess, pipeProcess and execute, respectively, except that they take a single command string and run it through the current user's default command interpreter. executeShell corresponds roughly to C's system function.

  • `kill` attempts to terminate a running `process`.
    
    

The following table compactly summarises the different process creation functions and how they relate to each other:

Runs program directly
Runs shell command
Low-level process creation
spawnProcess
spawnShell
Automatic input/output redirection using pipes
pipeProcess
pipeShell
Execute and wait for completion, collect output
execute
executeShell

Other functionality

  • `pipe` is used to create unidirectional pipes.
    
  • `environment` is an interface through which the current `process`'
        

    environment variables can be read and manipulated.

  • `escapeShellCommand` and `escapeShellFileName` are useful
        

    for constructing shell command lines in a portable way.

Source

std/process.d

Note

Most of the functionality in this module is not available on iOS, tvOS and watchOS. The only functions available on those platforms are: environment, thisProcessID and thisThreadID.

@authorsLars Tandle Kyllingstad, Steven Schveighoffer, Vladimir Panteleev@copyrightCopyright (c) 2013, the authors. All rights reserved.@licenseBoost License 1.0.
process
:
(class) std.process.environment

Manipulates environment variables using an associative-array-like interface.

This class contains only static methods, and cannot be instantiated. See below for examples of use.

environment
;
const
(local variable) const(string) leg
leg
=
(class) std.process.environment

Manipulates environment variables using an associative-array-like interface.

This class contains only static methods, and cannot be instantiated. See below for examples of use.

environment
.
string std.process.environment.get(scope const(char)[] name, string defaultValue = null) @safe

Retrieves the value of the environment variable with the given name, or a default value if the variable doesn't exist.

Unlike environment.opIndex, this function never throws on Posix.

auto sh = environment.get("SHELL", "/bin/sh");

This function is also useful in checking for the existence of an environment variable.

auto myVar = environment.get("MYVAR");
if (myVar is null)
{
    // Environment variable doesn't exist.
    // Note that we have to use 'is' for the comparison, since
    // myVar == null is also true if the variable exists but is
    // empty.
}
@paramname name of the environment variable to retrieve@paramdefaultValue default value to return if the environment variable doesn't exist.@returnsthe value of the environment variable if found, otherwise null if the environment doesn't exist.@throwsUTFException if the variable contains invalid UTF-16 characters (Windows only).
get
(
(constant) string gc_uaf_blindspot.childVar = "GC_UAF_BLINDSPOT_CHILD"

Name of the env var that selects the faulty child leg.

childVar
);
if (
(local variable) const(string) leg
leg
== "gc")
return
int gc_uaf_blindspot.runGcLeg()

Child leg 1: use-after-free entirely inside GC-managed memory. ASan never sees the GC's mmap'd pools, so this runs to completion.

runGcLeg
();
if (
(local variable) const(string) leg
leg
== "malloc")
return
int gc_uaf_blindspot.runMallocLeg()

Child leg 2: the identical bug on the intercepted C allocator. ASan poisons freed memory, so this read aborts with a report.

runMallocLeg
();
static if (!instrumented) {
void std.stdio.writeln!(string, string)(string __param_0, string __param_1) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("SKIP: built without AddressSanitizer instrumentation ",
"(DMD has no -fsanitize; LDC-only dflags are platform-gated)"); return 0; } else { // Parent: run both legs and verify the contrast. const gc = runChild("gc"); assert(gc.status == 0, "expected the GC use-after-free to go UNdetected (the blind spot)"); assert(!gc.output.canFind("AddressSanitizer"), "expected no ASan report for GC memory"); writeln("proved: use-after-free inside GC pools is invisible to ASan"); const mal = runChild("malloc"); assert(mal.status != 0, "expected ASan to abort the malloc/free use-after-free child"); assert(mal.output.canFind("heap-use-after-free"), "expected a heap-use-after-free report; got:\n" ~ mal.output); writefln("proved: the same bug on malloc/free dies with " ~ "heap-use-after-free (child exit %s)", mal.status); writeln("OK: ASan sees the C heap but not the GC's mmap'd pools"); return 0; } } int
int D main()
main
()
{ version (
linux
linux
)
return
int gc_uaf_blindspot.run()
run
();
else { writeln("SKIP: this probe records Linux behavior (platforms \"linux\")"); return 0; } }