Gallopsled / pwntools

CTF framework and exploit development library
http://pwntools.com
Other
11.74k stars 1.67k forks source link

`tube.readline()` skips the last line if it's not terminated with a newline #2366

Closed mkow closed 3 months ago

mkow commented 4 months ago

I recently missed a flag in a CTF, because it seems that tube.readline() returns data only if it actually ended with a newline. If an EOF happens (even after some data was already read) it throws EOFError instead of returning the line. I'm not sure how the API should ideally look like, but it seems quite dangerous in the current form, because e.g. this simple loop:

try:
    print(r.readline(keepends=False))
except EOFError:
    pass

will silently hide the last line of data if it's not newline-terminated, despite the code looking correct. Also, this quirk doesn't seem to be documented currently.

Additional argument why it's confusing: the standard recv/send semantics, probably in all runtimes/languages, raises EOF exception / signal / 0 return only if all the data was already consumed and someone retried reading.

peace-maker commented 4 months ago

Hm, the docs define a "line" as a sequence of bytes terminated by the tube.newline sequence.

Is it really a line if it doesn't end with the desired newline character? I guess eof is a special case but having recvline not receive a line sounds weird. Maybe return everything buffered on eof but that feels like a breaking change?

Your usecase sounds like a job for tube.stream().

CptGibbon commented 4 months ago

If you think it's relevant, GLIBC's readline() considers EOF a newline if data preceded it 🤷‍♂️

peace-maker commented 4 months ago

True, it's better to not lose data. @Arusekk ?