1

I have some segfault in my application and what I've seen happening is that "vsnprintf" always fails(it returns negative number, in my case "-1").

This is on aarch64 (arm64).

The segfault is because of some bad implementation in a third party library but I want to know what are the possible reasons of vsnprintf failing. Also, is there a way to see errno or the reason of vsnprintf fail with GDB in a core dump? I don't have the process running.

This is the relevant implementation:

int mg_printf(const char *fmt, ...) {
  int len;
  char mem[100];
  va_list ap;
  va_start(ap, fmt);
  len = vsnprintf(mem, sizeof(mem), fmt, ap); // this fails all the time
  va_end(ap);
  return len;
}

// len is very sketchy
int len = 1602988490;
const char* pr = "[aaaa:bbbb:0000:0000:0000:0000:0000:0010]:12345";
mg_printf("Host: %.*s\r\n", len, pr);

So, as you can see, the "len" provided to vsnprintf in the va_list is very "sketchy". Can this be the reason of the fail? This "len" value is not intended but it's the result of some other calls.

w0wy
  • 21
  • 3
  • Why do you think it fails? It looks ok to me. https://godbolt.org/z/KdYaM1188 – Ted Lyngmo Jul 14 '22 at 11:46
  • Where do you get that number for `len`? Do you have a breakpoint in `mg_printf`? Also it is assigned the value that is returned from `vsnprintf` which you claim always is `-1`. – Gerhardh Jul 14 '22 at 11:47
  • @TedLyngmo this is just a very reduced snipped of the full code. I already tried this on godbolt and I know it works. Also, this code is on arm64. – w0wy Jul 14 '22 at 11:54
  • 1
    @w0wy We can't help if we don't see code that reproduces the error you get. Please provide a [mre] – Ted Lyngmo Jul 14 '22 at 11:56
  • @Gerhardh it is code in third party library (mongoose) and it is because of some pointer arithmetic(const char* - const char*). Related to the other mentions: I'm only able to do postmortem debugging with core dumps(so, no breakpoints). If I had possibility to debug live, it would've been much easier. I'm mostly curious why could vsnprintf fail. – w0wy Jul 14 '22 at 11:57
  • @TedLyngmo understood and I agree. To be honest, if I could reproduce it, I would've probably found the issue also. My main question in this case would be: why could vsnprintf fail? Is there some "usual" failure that can happen? Something standard? – w0wy Jul 14 '22 at 11:59
  • 1
    *The segfault is because of some bad implementation in a third party library.* Do you have proof of that? It is almost always the case that the fault is in one's own code, but has not yet been identified. – Weather Vane Jul 14 '22 at 12:00
  • If the real code has invalid conversion specifiers and/or non-matching arguments, sure, it could crash – Ted Lyngmo Jul 14 '22 at 12:00
  • 1
    specing `1602988490` as a width specifier for outputting `"[aaaa:bbbb:0000:0000:0000:0000:0000:0010]:12345"` seems excessive. Can you fill in any more details regarding: _"This "len" value is not intended but it's the result of some other calls"_ ?? – ryyker Jul 14 '22 at 12:31
  • Is it possible that it is being fed a buffer containing multibyte or wide characters? [from question/answer here](https://stackoverflow.com/questions/2948361/when-and-why-can-sprintf-fail) – ryyker Jul 14 '22 at 12:44
  • @WeatherVane yes. Here is what I meant: https://github.com/cesanta/mongoose/blob/6.11/mongoose.c at line 2031, len is -1 and it crashes. This happens because vsnprintf fails everytime and eventually, the malloc will fail since size of it is always doubled in the while loop. Free buffer -> try malloc double size -> if success try vsnprintf again -> if not success set len to -1 -> access buffer[-1]. – w0wy Jul 14 '22 at 13:07
  • @ryyker yes. It happens here: https://github.com/cesanta/mongoose/blob/6.11/mongoose.c at line 10184. path.p is NULL (0x0) and host.p is [aaaa:bbbb:0000:0000:0000:0000:0000:0010]:12345. These are both const char*. – w0wy Jul 14 '22 at 13:10
  • `len` is not `-1` at line 2031 `(*buf)[len] = 0;`. It was `< 0` at the start of the code block, but at line 2012 `while (len < 0)` ensures that it is not. – Weather Vane Jul 14 '22 at 13:30
  • @WeatherVane no. This is only as long as malloc succeeds everytime. If malloc fails, len will be -1. Lines 2019 and 2020. If size is doubled continuously, malloc is bound to fail. Afaik, it can only alloc up to 2GB at a time. Max value of size_t(unsigned int) is higher than 2GB(in bytes). – w0wy Jul 14 '22 at 14:04
  • You seem to be right. – Weather Vane Jul 14 '22 at 14:08

1 Answers1

2

So, as you can see, the "len" provided to vsnprintf in the va_list is very "sketchy". Can this be the reason of the fail? This "len" value is not intended but it's the result of some other calls.

Yes.

printf() and friends are subject to an environmental limitation.

The number of characters that can be produced by any single conversion shall be at least 4095. C17dr § 7.21.6.1 15

Any conversion above 4095 risks problems. Limit len.

// mg_printf("Host: %.*s\r\n", len, pr);
mg_printf("Host: %.*s\r\n", len > 4000 ? 4000 : len, pr);

Or perhaps user code (that does not use *printf()) to print the string with a huge 1602988490 limit.

mg_printf("Host: ");

for (int i = 0; i < len && pr[i]; i++) putchar(pr[i]);

mg_printf("\r\n");

Also, is there a way to see errno or the reason of vsnprintf ...

In general, vsnprintf() is not specified to set errno on any condition. Select implementations may do so though.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256