Skip to content

Commit de5fe52

Browse files
committed
everything is patched (i think)
1 parent ed78907 commit de5fe52

File tree

8 files changed

+69
-38
lines changed

8 files changed

+69
-38
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ free(ptr)
3535
- Fully type safe
3636
- Pythonic pointer API
3737
- Bindings for the entire C standard library
38-
- Segfaults
38+
- Undefined behavior
3939

4040
### Why does this exist?
4141

src/mod.c

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#include <stdio.h>
77
#include <frameobject.h> // needed to get members of PyFrameObject
88
#define GETOBJ PyObject* obj; if (!PyArg_ParseTuple(args, "O", &obj)) return NULL
9+
#define INIT_HANDLER(handle, msg) if (signal(SIGSEGV, handle) == SIG_ERR) { \
10+
PyErr_SetString(PyExc_ImportError, msg); \
11+
return NULL; \
12+
}
913
static jmp_buf buf;
1014

1115
static PyObject* add_ref(PyObject* self, PyObject* args) {
@@ -41,10 +45,14 @@ static PyObject* force_set_attr(PyObject* self, PyObject* args) {
4145
Py_RETURN_NONE;
4246
}
4347

44-
void handler(int signum) {
48+
static void sigsegv_handler(int signum) {
4549
longjmp(buf, 1);
4650
}
4751

52+
static void sigiot_handler(int signum) {
53+
longjmp(buf, 2);
54+
}
55+
4856
static PyObject* handle(PyObject* self, PyObject* args) {
4957
PyObject* func;
5058
PyObject* params = NULL;
@@ -63,21 +71,31 @@ static PyObject* handle(PyObject* self, PyObject* args) {
6371

6472
if (!params) params = PyTuple_New(0);
6573
if (!kwargs) kwargs = PyDict_New();
74+
int val = setjmp(buf);
6675

67-
if (setjmp(buf)) {
76+
if (val) {
6877
PyFrameObject* frame = PyEval_GetFrame();
69-
PyCodeObject* code = frame->f_code;
70-
Py_INCREF(code);
78+
PyObject* name;
79+
PyCodeObject* code = NULL;
80+
81+
if (frame) {
82+
code = frame->f_code;
83+
Py_INCREF(code);
84+
name = code->co_name;
85+
} else {
86+
name = PyObject_GetAttrString(func, "__name__");
87+
}
7188

7289
// this is basically a copy of PyFrame_GetCode, which is only available on 3.9+
7390

7491
PyErr_Format(
7592
PyExc_RuntimeError,
76-
"segment violation occured during execution of %S",
77-
code->co_name
93+
"%s occured during execution of %S",
94+
val == 1 ? "segment violation" : "python aborted",
95+
name
7896
);
7997

80-
Py_DECREF(code);
98+
if (code) Py_DECREF(code);
8199
return NULL;
82100
}
83101

@@ -105,10 +123,14 @@ static struct PyModuleDef module = {
105123
};
106124

107125
PyMODINIT_FUNC PyInit__pointers(void) {
108-
if (signal(SIGSEGV, handler) == SIG_ERR) {
109-
PyErr_SetString(PyExc_RuntimeError, "failed to setup SIGSEGV handler");
110-
return NULL;
111-
}
126+
INIT_HANDLER(
127+
sigiot_handler,
128+
"cant load _pointers: failed to setup SIGIOT handler"
129+
);
130+
INIT_HANDLER(
131+
sigsegv_handler,
132+
"cant load _pointers: failed to setup SIGSEGV handler"
133+
);
112134

113135
return PyModule_Create(&module);
114136
}

src/pointers/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
from .custom_binding import binding, binds
1919
from .decay import decay, decay_annotated, decay_wrapped
2020
from .exceptions import (
21-
AllocationError, DereferenceError, FreedMemoryError,
22-
InvalidBindingParameter, InvalidSizeError
21+
Aborted, AllocationError, DereferenceError, FreedMemoryError,
22+
InvalidBindingParameter, InvalidSizeError, NullPointerError,
23+
SegmentViolation
2324
)
2425
from .magic import _
2526
from .malloc import AllocatedPointer, free, malloc, realloc

src/pointers/base_pointers.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import ctypes
2-
import faulthandler
32
import sys
43
import warnings
54
import weakref
65
from abc import ABC, abstractmethod
76
from contextlib import suppress
8-
from io import UnsupportedOperation
97
from typing import (
108
Any, Generic, Iterator, Optional, Tuple, Type, TypeVar, Union
119
)
@@ -33,11 +31,6 @@
3331
T = TypeVar("T")
3432
A = TypeVar("A", bound="BasicPointer")
3533

36-
with suppress(
37-
UnsupportedOperation
38-
): # in case its running in idle or something like that
39-
faulthandler.enable()
40-
4134

4235
class BasicPointer(ABC):
4336
"""Base class representing a pointer with no operations."""

src/pointers/c_pointer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ def _as_parameter_(self) -> ctypes.c_void_p:
3939

4040
@handle
4141
def dereference(self) -> Optional[int]:
42-
"""Dereference the pointer."""
4342
deref = ctypes.c_void_p.from_address(self.ensure())
4443
return deref.value
4544

src/pointers/constants.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from _pointers import handle as _handle
1111

12-
from .exceptions import SegmentViolation
12+
from .exceptions import Aborted, SegmentViolation
1313

1414
with suppress(
1515
UnsupportedOperation
@@ -56,15 +56,23 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
5656
return call
5757
except (RuntimeError, OSError) as e:
5858
msg = str(e)
59-
60-
if not msg.startswith("segment violation") and not msg.startswith(
61-
"access violation"
62-
):
59+
segv = any(
60+
{
61+
msg.startswith("segment violation"),
62+
msg.startswith("access violation"),
63+
}
64+
)
65+
66+
aborted = msg.startswith(
67+
"python aborted",
68+
)
69+
70+
if (not segv) and (not aborted):
6371
raise
6472

6573
with suppress(UnsupportedOperation):
6674
faulthandler.enable()
6775

68-
raise SegmentViolation(str(e)) from None
76+
raise (SegmentViolation if segv else Aborted)(str(e)) from None
6977

7078
return wrapper

src/pointers/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"NullPointerError",
88
"InvalidVersionError",
99
"SegmentViolation",
10+
"Aborted",
1011
)
1112

1213

@@ -56,3 +57,9 @@ class SegmentViolation(Exception):
5657
"""SIGSEGV was sent to Python."""
5758

5859
pass
60+
61+
62+
class Aborted(Exception):
63+
"""SIGIOT/SIGABRT was sent to Python."""
64+
65+
pass

tests/test_pointer.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
from ward import raises, test
44

5-
from _pointers import handle
6-
from pointers import NULL, InvalidSizeError, Pointer
5+
from _pointers import add_ref
6+
from pointers import NULL, InvalidSizeError, Pointer, SegmentViolation
77
from pointers import _ as m
8-
from pointers import strlen, to_c_ptr, to_ptr
8+
from pointers import handle, to_c_ptr, to_ptr
99
from pointers.exceptions import NullPointerError
1010

1111

@@ -37,14 +37,14 @@ def _():
3737

3838
@test("movement")
3939
def _():
40-
a = "teststring"
40+
a = 763723
4141
ptr = to_ptr(a)
42-
ptr <<= "hi"
42+
ptr <<= 2
4343

44-
assert a == "hi"
44+
assert a == 2
4545

4646
with raises(InvalidSizeError):
47-
ptr <<= "hello world"
47+
ptr <<= 758347580937450893
4848

4949

5050
@test("assignment with tracked types")
@@ -78,7 +78,7 @@ def _():
7878
with raises(NullPointerError):
7979
print(~to_ptr(NULL))
8080

81-
ptr2 = to_ptr(NULL)
81+
ptr2: Pointer[int] = to_ptr(NULL)
8282
ptr2 >>= 1
8383

8484
ptr2 >>= NULL
@@ -93,8 +93,9 @@ def _():
9393

9494
@test("segfault handler")
9595
def _():
96+
@handle
9697
def segfault():
9798
ctypes.string_at(0)
9899

99-
with raises(RuntimeError):
100-
handle(segfault)
100+
with raises(SegmentViolation):
101+
segfault()

0 commit comments

Comments
 (0)