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