Skip to content

openvasd expands port ranges into per-port list → nmap E2BIG ("Argument list too long") on full 1-65535 scans #2223

Description

@arch-fan

Summary

When a scan's target.ports contains a large range, openvasd expands that range into an explicit, per-port comma-separated list when building the openvas port_range preference, instead of keeping it compact (start-end). For a full TCP scan (1-65535) the resulting string is ~380 KB. nmap.nasl passes that whole string to nmap as a single -p <port_range> argument, which exceeds the Linux kernel's per-argument limit (MAX_ARG_STRLEN, 128 KB), so execve fails with E2BIG:

nasl_pread: Failed to execute child process "nmap" (Argument list too long)

The port scan therefore never runs. The scan completes "successfully" but reports zero open ports for every host — only host-level results (OS detection, ICMP, traceroute). No service detection, no service-based VTs, no findings.

Affected component

  • openvas-scanner 23.45.5 (openvasd + C openvas backend, [scanner] type = "openvas").
  • Function: ports_to_openvas_port_list in rust/crates/greenbone-scanner-framework/src/models/port.rs.

Root cause

add_range_to_list enumerates every port in the range:

fn add_range_to_list(list: &mut String, start: usize, end: Option<usize>) {
    if let Some(end) = end {
        for p in start..=end {                 // expands 1..=65535 into "1,2,3,...,65535,"
            list.push_str(p.to_string().as_str());
            list.push(',');
        }
    } else {
        list.push_str(start.to_string().as_str());
        list.push(',');
    }
}

Notably, the PortRange struct in the same file already has a Display impl that produces the compact form:

impl Display for PortRange {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.end {
            Some(end) => write!(f, "{}-{}", self.start, end),
            None => write!(f, "{}", self.start),
        }
    }
}

ports_to_openvas_port_list just doesn't use it. nmap.nasl itself is fine — it reads get_preference("port_range") and passes it verbatim to nmap -p, which accepts compact ranges (-p T:1-65535).

Reproduction

POST a scan with a full TCP range as structured ports, scanner type openvas:

{
  "target": {
    "hosts": ["<reachable host>"],
    "ports": [{ "protocol": "tcp", "range": [{ "start": 1, "end": 65535 }] }]
  },
  "vts": [ ... full-and-fast ... ]
}

Start it and watch the journal:

openvasd[...]: nasl_pread: Failed to execute child process "nmap" (Argument list too long)

GET /scans/<id>/results then contains no open ports for any host, even hosts with services listening. Using a smaller list (e.g. "All IANA assigned TCP", ~5,836 ports → ~35 KB when expanded) stays under the 128 KB single-arg limit and works, which is why the bug only shows up on large/all-port scans.

Confirming it is the port_range string: injecting a compact port_range scan preference ("T:1-65535") with empty structured target.ports makes the scan run nmap correctly and detect ports — no E2BIG.

Impact

Full-port / all-TCP scans driven via openvasd's structured target.ports silently perform no port scanning and detect nothing service-side — a false-negative across the whole scan. gvmd/ospd-openvas are unaffected because they pass a compact port_range string rather than structured ranges.

Suggested fix

Emit the compact form in add_range_to_list (reuse PortRange's Display), e.g.:

fn add_range_to_list(list: &mut String, start: usize, end: Option<usize>) {
    match end {
        Some(end) if end != start => list.push_str(&format!("{start}-{end},")),
        _ => list.push_str(&format!("{start},")),
    }
}

This keeps T:1-65535 compact and well within MAX_ARG_STRLEN.


This issue was investigated and filed automatically by Claude (an AI assistant), at the request of a user running openvas-scanner 23.45.5, based on hands-on debugging (journal traces, source inspection, and a compact-port_range workaround that confirms the root cause).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions