Skip to content

Commit cd31f3a

Browse files
committed
Annotate ReadBuffer.read_bytes() with an explicit return type
Not doing so makes Cython wrap the result with __Pyx_PyObject_AsWritableString, which causes data loss if there is a NULL-byte in the middle of the returned char* buffer. While at it, constify the pointers returned by ReadBuffer methods, as the returned data is not supposed to be modified. Fixes: #158.
1 parent 656c39a commit cd31f3a

File tree

3 files changed

+26
-23
lines changed

3 files changed

+26
-23
lines changed

asyncpg/protocol/buffer.pxd

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77

88
cdef class Memory:
99
cdef:
10-
char* buf
10+
const char *buf
1111
object owner
1212
ssize_t length
1313

1414
cdef as_bytes(self)
1515

1616
@staticmethod
17-
cdef inline Memory new(char* buf, object owner, ssize_t length)
17+
cdef inline Memory new(const char *buf, object owner, ssize_t length)
1818

1919

2020
cdef class WriteBuffer:
@@ -98,16 +98,16 @@ cdef class ReadBuffer:
9898
cdef inline _ensure_first_buf(self)
9999
cdef _switch_to_next_buf(self)
100100
cdef inline read_byte(self)
101-
cdef inline char* _try_read_bytes(self, ssize_t nbytes)
101+
cdef inline const char* _try_read_bytes(self, ssize_t nbytes)
102102
cdef inline _read(self, char *buf, ssize_t nbytes)
103103
cdef read(self, ssize_t nbytes)
104-
cdef inline read_bytes(self, ssize_t n)
104+
cdef inline const char* read_bytes(self, ssize_t n) except NULL
105105
cdef inline read_int32(self)
106106
cdef inline read_int16(self)
107107
cdef inline read_cstr(self)
108108
cdef int32_t has_message(self) except -1
109109
cdef inline int32_t has_message_type(self, char mtype) except -1
110-
cdef inline char* try_consume_message(self, ssize_t* len)
110+
cdef inline const char* try_consume_message(self, ssize_t* len)
111111
cdef Memory consume_message(self)
112112
cdef bytearray consume_messages(self, char mtype)
113113
cdef discard_message(self)

asyncpg/protocol/buffer.pyx

+19-16
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ cdef class Memory:
2121
return cpython.PyBytes_FromStringAndSize(self.buf, self.length)
2222

2323
@staticmethod
24-
cdef inline Memory new(char* buf, object owner, ssize_t length):
24+
cdef inline Memory new(const char* buf, object owner, ssize_t length):
2525
cdef Memory mem
2626
mem = Memory.__new__(Memory)
2727
mem.buf = buf
@@ -295,7 +295,7 @@ cdef class ReadBuffer:
295295
raise RuntimeError(
296296
'debug: second buffer of ReadBuffer is empty')
297297

298-
cdef inline char* _try_read_bytes(self, ssize_t nbytes):
298+
cdef inline const char* _try_read_bytes(self, ssize_t nbytes):
299299
# Try to read *nbytes* from the first buffer.
300300
#
301301
# Returns pointer to data if there is at least *nbytes*
@@ -305,7 +305,7 @@ cdef class ReadBuffer:
305305
# to calling try_read_bytes, and must not overread
306306

307307
cdef:
308-
char * result
308+
const char *result
309309

310310
if ASYNCPG_DEBUG:
311311
if nbytes > self._length:
@@ -353,12 +353,13 @@ cdef class ReadBuffer:
353353
cdef:
354354
bytearray result
355355
ssize_t nread
356+
const char *cbuf
356357
char *buf
357358

358359
self._ensure_first_buf()
359-
buf = self._try_read_bytes(nbytes)
360-
if buf != NULL:
361-
return Memory.new(buf, self._buf0, nbytes)
360+
cbuf = self._try_read_bytes(nbytes)
361+
if cbuf != NULL:
362+
return Memory.new(cbuf, self._buf0, nbytes)
362363

363364
if nbytes > self._length:
364365
raise BufferError(
@@ -376,7 +377,7 @@ cdef class ReadBuffer:
376377
return Memory.new(buf, result, nbytes)
377378

378379
cdef inline read_byte(self):
379-
cdef char* first_byte
380+
cdef const char *first_byte
380381

381382
if ASYNCPG_DEBUG:
382383
if not self._buf0:
@@ -390,10 +391,10 @@ cdef class ReadBuffer:
390391

391392
return first_byte[0]
392393

393-
cdef inline read_bytes(self, ssize_t n):
394+
cdef inline const char* read_bytes(self, ssize_t n) except NULL:
394395
cdef:
395396
Memory mem
396-
char *cbuf
397+
const char *cbuf
397398

398399
self._ensure_first_buf()
399400
cbuf = self._try_read_bytes(n)
@@ -406,7 +407,7 @@ cdef class ReadBuffer:
406407
cdef inline read_int32(self):
407408
cdef:
408409
Memory mem
409-
char *cbuf
410+
const char *cbuf
410411

411412
self._ensure_first_buf()
412413
cbuf = self._try_read_bytes(4)
@@ -419,7 +420,7 @@ cdef class ReadBuffer:
419420
cdef inline read_int16(self):
420421
cdef:
421422
Memory mem
422-
char *cbuf
423+
const char *cbuf
423424

424425
self._ensure_first_buf()
425426
cbuf = self._try_read_bytes(2)
@@ -439,8 +440,8 @@ cdef class ReadBuffer:
439440
ssize_t pos
440441
ssize_t nread
441442
bytes result
442-
char* buf
443-
char* buf_start
443+
const char *buf
444+
const char *buf_start
444445

445446
self._ensure_first_buf()
446447

@@ -487,7 +488,7 @@ cdef class ReadBuffer:
487488

488489
cdef int32_t has_message(self) except -1:
489490
cdef:
490-
char* cbuf
491+
const char *cbuf
491492

492493
if self._current_message_ready:
493494
return 1
@@ -524,8 +525,10 @@ cdef class ReadBuffer:
524525
cdef inline int32_t has_message_type(self, char mtype) except -1:
525526
return self.has_message() and self.get_message_type() == mtype
526527

527-
cdef inline char* try_consume_message(self, ssize_t* len):
528-
cdef ssize_t buf_len
528+
cdef inline const char* try_consume_message(self, ssize_t* len):
529+
cdef:
530+
ssize_t buf_len
531+
const char *buf
529532

530533
if not self._current_message_ready:
531534
return NULL

asyncpg/protocol/coreproto.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ cdef class CoreProtocol:
394394

395395
cdef _parse_msg_command_complete(self):
396396
cdef:
397-
char* cbuf
397+
const char* cbuf
398398
ssize_t cbuf_len
399399

400400
cbuf = self.buffer.try_consume_message(&cbuf_len)
@@ -465,7 +465,7 @@ cdef class CoreProtocol:
465465
list rows
466466
decode_row_method decoder = <decode_row_method>self._decode_row
467467

468-
char* cbuf
468+
const char* cbuf
469469
ssize_t cbuf_len
470470
object row
471471
Memory mem

0 commit comments

Comments
 (0)