Skip to content

[Overflow Bug]RTKLIB streamsvr.c: current-head intra-object overflow in strconvnew() option formatting #794

Description

@neosys007
I would like to report a current-head `Intra-Object Overflow` in `src/streamsvr.c`, in the exported stream-converter constructor:

```c
extern strconv_t *strconvnew(int itype, int otype, const char *msgs, int staid,
                             int stasel, const char *opt)
```

The relevant current-head code is:

```c
sprintf(conv->rtcm.opt,"-EPHALL %s",opt);
sprintf(conv->raw.opt ,"-EPHALL %s",opt);
```

The relevant object layout from `rtklib.h` is:

```c
typedef struct {
    ...
    char opt[256];
} rtcm_t;

typedef struct {
    ...
    char opt[256];
    double receive_time;
    unsigned int plen;
    ...
} raw_t;

typedef struct {
    ...
    rtcm_t rtcm;
    raw_t raw;
    rtcm_t out;
} strconv_t;
```

Why I believe this is a real current-head bug:

1. `strconvnew()` is an exported API, not a test-only helper.

2. The caller-controlled `opt` string is used directly, and there is no current-head length validation before either formatter.

3. Both destination fields are fixed-width `256`-byte embedded members.

4. The first write targets `conv->rtcm.opt`, which is the tail of the embedded `rtcm_t` object and is followed immediately by the next embedded object `conv->raw`.

5. The second write targets `conv->raw.opt`, which is followed immediately by the live field `conv->raw.receive_time`.

So an oversized `opt` causes two concrete intra-object writes:

- `conv->rtcm.opt[256]` into adjacent `conv->raw`
- `conv->raw.opt[256]` into adjacent `conv->raw.receive_time`

This is not a generic "`sprintf` is dangerous" claim. The current code appends the fixed prefix `"-EPHALL "` to the user-supplied `opt` and stores the result in fixed `256`-byte struct fields without checking whether the formatted result fits.

A minimal trigger is already short:

- if `strlen(opt) == 255`
- then the formatted string length becomes `263`
- and the total write including the terminating NUL becomes `264`

That is enough to overflow both sinks.

I also built a reduced source-faithful proof preserving the exact relevant `gtime_t`, `rtcm_t`, `raw_t`, and `strconv_t` field ordering and the exact `"-EPHALL %s"` formatting pattern. The output is:

```text
opt_len=255
formatted_len=263
distance_rtcm_opt_to_raw=260
distance_raw_opt_to_receive_time=256
rtcm_overflow_bytes_into_raw=4
raw_overflow_bytes_into_receive_time=8
raw_time_prefix_hex=4646460022222222
receive_time_prefix_hex=4646464646464600
out_unchanged=1
```

That result shows:

- the first formatter crosses from `rtcm.opt` into the next embedded `raw` object
- the second formatter overwrites the entire adjacent `receive_time` field
- the overwrite remains localized and does not depend on also reaching the following `out` object

I am making a narrow claim:

- this is current-head
- it is reachable through the exported `strconvnew()` API
- it is a real write-side intra-object overflow
- the concrete targets are `conv->rtcm.opt -> conv->raw` and `conv->raw.opt -> conv->raw.receive_time`

I think the right fix is to validate `opt` length before formatting and replace raw `sprintf()` with bounded formatting that rejects truncation.

Best regards
Pengpeng Hou
ISCAS

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions