DedSec Project

Failure handling and memory ownership in systems code — DedSec Project

Failure handling and memory ownership in systems code Bash: no failure handling Broken example — DO NOT USE Fixed example Why it fails and the repair works Only report success when the…

Bash: no failure handling

Broken example — DO NOT USE

cp "$src" "$dst"
echo "Backup complete"

Fixed example

if cp -- "$src" "$dst"; then
  echo "Backup complete"
else
  echo "Backup failed" >&2
  exit 1
fi

Why it fails and the repair works

Only report success when the operation succeeds.

Exercise: inject an I/O failure

The last command in a script determines its final exit status unless failures are handled. An unconditional success message can therefore conceal a failed copy. Test the actual operation and return nonzero when it fails.

#!/usr/bin/env bash
set -euo pipefail
tmp=$(mktemp -d)
src="$tmp/source.txt"
dst="$tmp/backup.txt"
trap 'rm -f -- "$src" "$dst"; rmdir -- "$tmp"' EXIT
printf 'verified content\n' > "$src"
backup() {
  if cp -- "$1" "$2"; then
    printf 'Backup complete\n'
  else
    printf 'Backup failed\n' >&2
    return 1
  fi
}
backup "$src" "$dst"
cmp -- "$src" "$dst"
if backup "$tmp/missing.txt" "$dst"; then
  exit 1
fi
cmp -- "$src" "$dst"
printf 'Expected failure detected; valid backup preserved\n'

Run bash backup_lab.sh. The deliberately missing source must fail while the earlier destination remains unchanged. set -e has conditional and pipeline exceptions; explicit status checks remain necessary. For pipelines use pipefail. For critical backups, write a temporary destination, verify it, and rename it on the same filesystem only after success.

C: unchecked input buffer

Broken example — DO NOT USE

char name[16];
scanf("%s", name);

Fixed example

char name[16];
if (fgets(name, sizeof name, stdin) == NULL) return 1;

Why it fails and the repair works

Use bounded reads and check the return value.

Complete C11 input-boundary lab

An unrestricted scanf("%s", name) may write past the array. fgets bounds the read and always terminates a successful string, but an overlong line can leave unread input for the next prompt. Reject the oversized case explicitly.

#include <stdio.h>
#include <string.h>
int main(void) {
    char name[16];
    if (!fgets(name, sizeof name, stdin)) return 1;
    size_t n = strcspn(name, "\n");
    if (name[n] == '\n') {
        name[n] = '\0';
    } else if (n == sizeof name - 1) {
        fputs("Input too long\n", stderr);
        return 2;
    }
    printf("Hello, %s\n", name);
    return 0;
}

Compile: cc -std=c11 -Wall -Wextra -pedantic input.c -o input. printf 'Ada\n' | ./input should succeed; printf 'abcdefghijklmnop\n' | ./input must return 2. Empty stdin returns 1. The exact maximum accepted length is 14 characters when newline is present: one byte is reserved for NUL. This deliberately conservative version rejects a 15-character line without newline; adapt EOF policy to your protocol. Use sanitizers to catch other memory defects, not as a substitute for bounds checks.

C++: ownership of resources

Broken example — DO NOT USE

int* p = new int(7);
// forgotten delete

Fixed example

auto p = std::make_unique<int>(7);
// include <memory>

Why it fails and the repair works

RAII releases memory when ownership goes out of scope.

Complete ownership lab (C++17)

The critical question is who releases the resource on every exit path, including exceptions. A unique_ptr expresses exclusive ownership; move transfers that ownership without duplicating it.

#include <cassert>
#include <memory>
#include <utility>
int main() {
    auto first = std::make_unique<int>(7);
    assert(*first == 7);
    auto second = std::move(first);
    assert(!first);
    assert(second && *second == 7);
    second.reset();
    assert(!second);
}

Compile: c++ -std=c++17 -Wall -Wextra -pedantic ownership.cpp -o ownership && ./ownership. For a negative compile-time test, try copying second into another unique_ptr: the compiler must reject the copy. Prefer stack values or vector when dynamic ownership is unnecessary; shared_ptr is appropriate only when multiple owners truly share lifetime.