cybersecurity · beginner · ~15 min · safe pentest lab

Usable Hosts in a CIDR Subnet

Validate an attacker-influenced numeric field (a CIDR prefix) before using it as a shift amount, avoiding shift-related undefined behavior and enforcing a deny-by-default range check.

Challenge

During a network recon phase you parse CIDR prefixes (like /24) from a scoping document to size the address ranges you are allowed to probe. The prefix length is attacker-influenced data: if a malformed or out-of-range value slips through, a naive 2^(32-prefix) shift can invoke undefined behavior (shift-by-negative or shift-by->=32) and produce a bogus host count that inflates your scan surface. The asset to protect here is correctness of the range math: deny by default and validate the prefix BEFORE using it as a shift amount.

Implement long usable_hosts(int prefix) returning the number of usable host addresses in an IPv4 subnet of that prefix: 2^(32-prefix) - 2 for prefix 0..30. A /31 and /32 have 0 usable hosts by this convention. Any invalid prefix (< 0 or > 32) must be rejected with -1.

Example

  • usable_hosts(24) -> 254
  • usable_hosts(30) -> 2
  • usable_hosts(33) -> -1

Edge cases to handle: /0 (the full 2^32-2 space), /30 (2 hosts), the /31 and /32 zero cases, and out-of-range or negative prefixes that must return -1 without shifting.

Input format

A single int prefix, the CIDR prefix length (network bits). May be any int value, including negative or > 32.

Output format

A long: the count of usable host addresses (2^(32-prefix) - 2) for 0..30, 0 for /31 and /32, or -1 for any invalid prefix.

Constraints

Use only fixed-width arithmetic; no I/O, no allocation. Never shift by a negative amount or by >= 32 bits. Prefix range is validated against 0..32 inclusive before any computation.

Starter code

long usable_hosts(int prefix){
    /* TODO: validate prefix range and compute 2^(32-prefix)-2.
       This insecure stub trusts input and returns a wrong count. */
    return ((long)1 << (32 - prefix)); /* no bounds check, no -2 */
}

Common mistakes

Shifting before validating, causing shift-by-negative or shift-by->=32 undefined behavior; using a 32-bit int for 1 << (32-prefix) and overflowing at /0; forgetting the -2 (network + broadcast); returning 2^1-2 = 0 for /31 by formula instead of the required 0-hosts convention (works out the same for /31 but /32 formula gives -1, which is wrong — special-case both); treating an out-of-range prefix as valid.

Edge cases to handle

prefix = 0 (2^32-2 = 4294967294 usable hosts); prefix = 30 (2 hosts); prefix = 31 and 32 (0 usable hosts by convention); prefix < 0 or > 32 (return -1); very large negative like -100000 (still -1, no shift performed).

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.