This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Heap Segmentation Fault
Type: crash Stage:
Components: ctypes Versions: Python 3.9
process
Status: open Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: thewb, zach.ware
Priority: normal Keywords:

Created on 2021-11-17 01:55 by thewb, last changed 2022-04-11 14:59 by admin.

Files
File name Uploaded Description Edit
debug.txt thewb, 2021-11-17 01:55
Messages (3)
msg406443 - (view) Author: Bill Borskey (thewb) Date: 2021-11-17 01:55
Dereferencing a python object that does not exist through ctypes caused a segfault in pymalloc_alloc. 

rdi: 0x0000000000000006  rsi: 0x0000000000000006 

0   org.python.python             	0x00000001081ee277 pymalloc_alloc + 74
1   org.python.python             	0x00000001081ed3dc _PyObject_Malloc + 17
2   org.python.python             	0x0000000108296a94 normalizestring + 55
3   org.python.python             	0x0000000108296540 _PyCodec_Lookup + 76

Looks like six was passed to the allocator, not sure exactly what was written to it, or if I can control that. But I guess it was more than six bytes. I don't have the id I used with the debug info above, but: I had a smallish list of characters, overwrote it with zeros, called del on it. I checked the value a couple times with ctypes. What I think happened is the garbage collector finally reclaimed the memory and crashed when I dereferenced again with the bad value.

I checked it out using 0xCC to get an idea of where the value was landing, and I'm overwriting rbx directly. But it doesn't get as far as above and segfaults at O_get rather than the allocator. 

I looked and I see this function:

static PyObject *
O_get(void *ptr, Py_ssize_t size)
{
    PyObject *ob = *(PyObject **)ptr;
    if (ob == NULL) {
        if (!PyErr_Occurred())
            /* Set an error if not yet set */
            PyErr_SetString(PyExc_ValueError,
                            "PyObject is NULL");
        return NULL;
    }
    Py_INCREF(ob);
    return ob;
}

Seems like the code is increasing the reference count on the non-existing python object at 0xCCCCCCCCCCCCCCCC and writing out of bounds. 

$ python3 -X dev
Python 3.9.2 (default, Mar 26 2021, 23:27:12) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.cast(0xCCCCCCCCCCCCCCCC, ctypes.py_object).value
Fatal Python error: Segmentation fault

(below does not have the heap debugger on it seemed to mess with results)

Current thread 0x0000000104b83dc0 (most recent call first):
  File "<stdin>", line 1 in <module>
Segmentation fault: 11

Thread 0 crashed with X86 Thread State (64-bit):
  rax: 0x000000010e1be8c0  rbx: 0xcccccccccccccccc  rcx: 0x000000010e2775c9  rdx: 0x000000010e283890
  rdi: 0x000000010e1be8c0  rsi: 0x0000000000000008  rbp: 0x00007ffee20f24e0  rsp: 0x00007ffee20f24d0
   r8: 0x0000000000000004   r9: 0x6a2bff3e46d2619c  r10: 0x000000010e1d4b80  r11: 0x000000010e1d4bb8
  r12: 0x000000010defc5e0  r13: 0x00007f94edc5c390  r14: 0x000000010e1e1b90  r15: 0x0000000000000000
  rip: 0x000000010e2775d7  rfl: 0x0000000000010286  cr2: 0x000000010e2730f3
msg406470 - (view) Author: Zachary Ware (zach.ware) * (Python committer) Date: 2021-11-17 14:08
In general, as soon as you touch ctypes you're on your own :).  ctypes has no protections for this kind of case, so you need to protect yourself.

If you came across this some way that *should* have been safe, please provide more information. But just passing a random memory address to ctypes is likely to cause segfaults or worse.
msg406472 - (view) Author: Bill Borskey (thewb) Date: 2021-11-17 15:11
No worries. I find bugs in my day job, thought this might be a useful segfault but it segfaults because it’s incrementing that reference count on the pyobj that don’t exist. So pretty lame. I did spend an hour tracking it down so I thought I’d let y’all know in case you wanted to fix it. 

Cheers 

> On Nov 17, 2021, at 6:08 AM, Zachary Ware <report@bugs.python.org> wrote:
> 
> 
> Zachary Ware <zachary.ware@gmail.com> added the comment:
> 
> In general, as soon as you touch ctypes you're on your own :).  ctypes has no protections for this kind of case, so you need to protect yourself.
> 
> If you came across this some way that *should* have been safe, please provide more information. But just passing a random memory address to ctypes is likely to cause segfaults or worse.
> 
> ----------
> nosy: +zach.ware
> resolution:  -> not a bug
> status: open -> pending
> 
> _______________________________________
> Python tracker <report@bugs.python.org>
> <https://bugs.python.org/issue45825>
> _______________________________________
History
Date User Action Args
2022-04-11 14:59:52adminsetgithub: 89983
2021-11-17 15:11:42thewbsetstatus: pending -> open

messages: + msg406472
2021-11-17 14:08:53zach.waresetstatus: open -> pending

nosy: + zach.ware
messages: + msg406470

resolution: not a bug
2021-11-17 01:55:46thewbcreate