mirror of
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
synced 2026-10-24 00:48:53 +03:00
PyIter_Next() returns NULL both at end-of-iteration and when an exception
is raised. The code breaks out of the loop without checking
PyErr_Occurred(), potentially swallowing Python exceptions and continuing
with a partially filled buffer.
Add the missing error check.
Fixes: b7ba732e6a ("treewide: libgpiod v2 implementation")
Assisted-by: Claude Opus 4.6
Reviewed-by: Vincent Fazio <vfazio@xes-inc.com>
Tested-by: Vincent Fazio <vfazio@xes-inc.com>
Link: https://patch.msgid.link/20260604-pre-2-3-fixes-v2-12-781bcaf47475@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
432 lines
9.1 KiB
C
432 lines
9.1 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
// SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
|
|
|
|
#include "internal.h"
|
|
|
|
typedef struct {
|
|
PyObject_HEAD;
|
|
struct gpiod_line_request *request;
|
|
unsigned int *offsets;
|
|
enum gpiod_line_value *values;
|
|
size_t num_lines;
|
|
struct gpiod_edge_event_buffer *buffer;
|
|
} request_object;
|
|
|
|
static void internal_request_release(request_object *self)
|
|
{
|
|
if (self->request){
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
gpiod_line_request_release(self->request);
|
|
Py_END_ALLOW_THREADS;
|
|
self->request = NULL;
|
|
}
|
|
}
|
|
|
|
static void request_finalize(request_object *self)
|
|
{
|
|
internal_request_release(self);
|
|
|
|
if (self->offsets)
|
|
PyMem_Free(self->offsets);
|
|
|
|
if (self->values)
|
|
PyMem_Free(self->values);
|
|
|
|
if (self->buffer)
|
|
gpiod_edge_event_buffer_free(self->buffer);
|
|
}
|
|
|
|
static PyObject *
|
|
request_chip_name(request_object *self, void *Py_UNUSED(ignored))
|
|
{
|
|
return PyUnicode_FromString(
|
|
gpiod_line_request_get_chip_name(self->request));
|
|
}
|
|
|
|
static PyObject *
|
|
request_num_lines(request_object *self, void *Py_UNUSED(ignored))
|
|
{
|
|
return PyLong_FromUnsignedLong(
|
|
gpiod_line_request_get_num_requested_lines(self->request));
|
|
}
|
|
|
|
static PyObject *request_offsets(request_object *self, void *Py_UNUSED(ignored))
|
|
{
|
|
PyObject *lines, *line;
|
|
unsigned int *offsets;
|
|
size_t num_lines, i;
|
|
int ret;
|
|
|
|
num_lines = gpiod_line_request_get_num_requested_lines(self->request);
|
|
|
|
offsets = PyMem_Calloc(num_lines, sizeof(unsigned int));
|
|
if (!offsets)
|
|
return PyErr_NoMemory();
|
|
|
|
gpiod_line_request_get_requested_offsets(self->request, offsets, num_lines);
|
|
|
|
lines = PyList_New(num_lines);
|
|
if (!lines) {
|
|
PyMem_Free(offsets);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < num_lines; i++) {
|
|
line = PyLong_FromUnsignedLong(offsets[i]);
|
|
if (!line) {
|
|
Py_DECREF(lines);
|
|
PyMem_Free(offsets);
|
|
return NULL;
|
|
}
|
|
|
|
ret = PyList_SetItem(lines, i, line);
|
|
if (ret) {
|
|
Py_DECREF(lines);
|
|
PyMem_Free(offsets);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
PyMem_Free(offsets);
|
|
return lines;
|
|
}
|
|
|
|
static PyObject *request_fd(request_object *self, void *Py_UNUSED(ignored))
|
|
{
|
|
return PyLong_FromLong(gpiod_line_request_get_fd(self->request));
|
|
}
|
|
|
|
static PyGetSetDef request_getset[] = {
|
|
{
|
|
.name = "chip_name",
|
|
.get = (getter)request_chip_name,
|
|
},
|
|
{
|
|
.name = "num_lines",
|
|
.get = (getter)request_num_lines,
|
|
},
|
|
{
|
|
.name = "offsets",
|
|
.get = (getter)request_offsets,
|
|
},
|
|
{
|
|
.name = "fd",
|
|
.get = (getter)request_fd,
|
|
},
|
|
{ }
|
|
};
|
|
|
|
static PyObject *
|
|
request_release(request_object *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
internal_request_release(self);
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static void clear_buffers(request_object *self)
|
|
{
|
|
memset(self->offsets, 0, self->num_lines * sizeof(unsigned int));
|
|
memset(self->values, 0, self->num_lines * sizeof(int));
|
|
}
|
|
|
|
static PyObject *request_get_values(request_object *self, PyObject *args)
|
|
{
|
|
PyObject *offsets, *values, *val, *type, *iter, *next;
|
|
Py_ssize_t num_offsets, pos;
|
|
int ret;
|
|
|
|
ret = PyArg_ParseTuple(args, "OO", &offsets, &values);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
num_offsets = PyObject_Size(offsets);
|
|
if (num_offsets < 0)
|
|
return NULL;
|
|
|
|
if (num_offsets > (Py_ssize_t)self->num_lines) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"number of offsets exceeds the number of requested lines");
|
|
return NULL;
|
|
}
|
|
|
|
iter = PyObject_GetIter(offsets);
|
|
if (!iter)
|
|
return NULL;
|
|
|
|
clear_buffers(self);
|
|
|
|
for (pos = 0;; pos++) {
|
|
next = PyIter_Next(iter);
|
|
if (!next) {
|
|
Py_DECREF(iter);
|
|
if (PyErr_Occurred())
|
|
return NULL;
|
|
break;
|
|
}
|
|
|
|
self->offsets[pos] = Py_gpiod_PyLongAsUnsignedInt(next);
|
|
Py_DECREF(next);
|
|
if (PyErr_Occurred()) {
|
|
Py_DECREF(iter);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
ret = gpiod_line_request_get_values_subset(self->request,
|
|
num_offsets,
|
|
self->offsets,
|
|
self->values);
|
|
Py_END_ALLOW_THREADS;
|
|
if (ret)
|
|
return Py_gpiod_SetErrFromErrno();
|
|
|
|
type = Py_gpiod_GetModuleAttrString("gpiod.line", "Value");
|
|
if (!type)
|
|
return NULL;
|
|
|
|
for (pos = 0; pos < num_offsets; pos++) {
|
|
val = PyObject_CallFunction(type, "i", self->values[pos]);
|
|
if (!val) {
|
|
Py_DECREF(type);
|
|
return NULL;
|
|
}
|
|
|
|
ret = PyList_SetItem(values, pos, val);
|
|
if (ret) {
|
|
Py_DECREF(type);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Py_DECREF(type);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *request_set_values(request_object *self, PyObject *args)
|
|
{
|
|
PyObject *values, *key, *val, *val_stripped;
|
|
Py_ssize_t pos = 0, index = 0;
|
|
int ret;
|
|
|
|
ret = PyArg_ParseTuple(args, "O", &values);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
if (PyObject_Size(values) > (Py_ssize_t)self->num_lines) {
|
|
PyErr_SetString(PyExc_ValueError,
|
|
"number of offsets exceeds the number of requested lines");
|
|
return NULL;
|
|
}
|
|
|
|
clear_buffers(self);
|
|
|
|
/* Note: pos may not be contiguous. */
|
|
while (PyDict_Next(values, &pos, &key, &val)) {
|
|
self->offsets[index] = Py_gpiod_PyLongAsUnsignedInt(key);
|
|
if (PyErr_Occurred())
|
|
return NULL;
|
|
|
|
val_stripped = PyObject_GetAttrString(val, "value");
|
|
if (!val_stripped)
|
|
return NULL;
|
|
|
|
self->values[index] = PyLong_AsLong(val_stripped);
|
|
Py_DECREF(val_stripped);
|
|
if (PyErr_Occurred())
|
|
return NULL;
|
|
|
|
index++;
|
|
}
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
ret = gpiod_line_request_set_values_subset(self->request,
|
|
index,
|
|
self->offsets,
|
|
self->values);
|
|
Py_END_ALLOW_THREADS;
|
|
if (ret)
|
|
return Py_gpiod_SetErrFromErrno();
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *request_reconfigure_lines(request_object *self, PyObject *args)
|
|
{
|
|
struct gpiod_line_config *line_cfg;
|
|
PyObject *line_cfg_obj;
|
|
int ret;
|
|
|
|
ret = PyArg_ParseTuple(args, "O", &line_cfg_obj);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
line_cfg = Py_gpiod_LineConfigGetData(line_cfg_obj);
|
|
if (!line_cfg)
|
|
return NULL;
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
ret = gpiod_line_request_reconfigure_lines(self->request, line_cfg);
|
|
Py_END_ALLOW_THREADS;
|
|
if (ret)
|
|
return Py_gpiod_SetErrFromErrno();
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *request_read_edge_events(request_object *self, PyObject *args)
|
|
{
|
|
PyObject *max_events_obj, *event_obj, *events, *type;
|
|
size_t max_events, num_events, i;
|
|
struct gpiod_edge_event *event;
|
|
int ret;
|
|
|
|
ret = PyArg_ParseTuple(args, "O", &max_events_obj);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
if (!Py_IsNone(max_events_obj)) {
|
|
max_events = PyLong_AsSize_t(max_events_obj);
|
|
if (PyErr_Occurred())
|
|
return NULL;
|
|
} else {
|
|
max_events = 64;
|
|
}
|
|
|
|
Py_BEGIN_ALLOW_THREADS;
|
|
ret = gpiod_line_request_read_edge_events(self->request,
|
|
self->buffer, max_events);
|
|
Py_END_ALLOW_THREADS;
|
|
if (ret < 0)
|
|
return Py_gpiod_SetErrFromErrno();
|
|
|
|
num_events = ret;
|
|
|
|
events = PyList_New(num_events);
|
|
if (!events)
|
|
return NULL;
|
|
|
|
type = Py_gpiod_GetModuleAttrString("gpiod.edge_event", "EdgeEvent");
|
|
if (!type) {
|
|
Py_DECREF(events);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < num_events; i++) {
|
|
event = gpiod_edge_event_buffer_get_event(self->buffer, i);
|
|
if (!event) {
|
|
Py_DECREF(events);
|
|
Py_DECREF(type);
|
|
return NULL;
|
|
}
|
|
|
|
event_obj = PyObject_CallFunction(type, "iKiii",
|
|
gpiod_edge_event_get_event_type(event),
|
|
gpiod_edge_event_get_timestamp_ns(event),
|
|
gpiod_edge_event_get_line_offset(event),
|
|
gpiod_edge_event_get_global_seqno(event),
|
|
gpiod_edge_event_get_line_seqno(event));
|
|
if (!event_obj) {
|
|
Py_DECREF(events);
|
|
Py_DECREF(type);
|
|
return NULL;
|
|
}
|
|
|
|
ret = PyList_SetItem(events, i, event_obj);
|
|
if (ret) {
|
|
Py_DECREF(events);
|
|
Py_DECREF(type);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Py_DECREF(type);
|
|
return events;
|
|
}
|
|
|
|
static PyMethodDef request_methods[] = {
|
|
{
|
|
.ml_name = "release",
|
|
.ml_meth = (PyCFunction)request_release,
|
|
.ml_flags = METH_NOARGS,
|
|
},
|
|
{
|
|
.ml_name = "get_values",
|
|
.ml_meth = (PyCFunction)request_get_values,
|
|
.ml_flags = METH_VARARGS,
|
|
},
|
|
{
|
|
.ml_name = "set_values",
|
|
.ml_meth = (PyCFunction)request_set_values,
|
|
.ml_flags = METH_VARARGS,
|
|
},
|
|
{
|
|
.ml_name = "reconfigure_lines",
|
|
.ml_meth = (PyCFunction)request_reconfigure_lines,
|
|
.ml_flags = METH_VARARGS,
|
|
},
|
|
{
|
|
.ml_name = "read_edge_events",
|
|
.ml_meth = (PyCFunction)request_read_edge_events,
|
|
.ml_flags = METH_VARARGS,
|
|
},
|
|
{ }
|
|
};
|
|
|
|
PyTypeObject request_type = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
.tp_name = "gpiod._ext.Request",
|
|
.tp_basicsize = sizeof(request_object),
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
.tp_finalize = (destructor)request_finalize,
|
|
.tp_dealloc = (destructor)Py_gpiod_dealloc,
|
|
.tp_getset = request_getset,
|
|
.tp_methods = request_methods,
|
|
};
|
|
|
|
PyObject *Py_gpiod_MakeRequestObject(struct gpiod_line_request *request,
|
|
size_t event_buffer_size)
|
|
{
|
|
struct gpiod_edge_event_buffer *buffer;
|
|
enum gpiod_line_value *values;
|
|
request_object *req_obj;
|
|
unsigned int *offsets;
|
|
size_t num_lines;
|
|
|
|
num_lines = gpiod_line_request_get_num_requested_lines(request);
|
|
|
|
req_obj = PyObject_New(request_object, &request_type);
|
|
if (!req_obj)
|
|
return NULL;
|
|
|
|
offsets = PyMem_Calloc(num_lines, sizeof(unsigned int));
|
|
if (!offsets) {
|
|
Py_DECREF(req_obj);
|
|
return NULL;
|
|
}
|
|
|
|
values = PyMem_Calloc(num_lines, sizeof(int));
|
|
if (!values) {
|
|
PyMem_Free(offsets);
|
|
Py_DECREF(req_obj);
|
|
return NULL;
|
|
}
|
|
|
|
buffer = gpiod_edge_event_buffer_new(event_buffer_size);
|
|
if (!buffer) {
|
|
PyMem_Free(values);
|
|
PyMem_Free(offsets);
|
|
Py_DECREF(req_obj);
|
|
return Py_gpiod_SetErrFromErrno();
|
|
}
|
|
|
|
req_obj->request = request;
|
|
req_obj->offsets = offsets;
|
|
req_obj->values = values;
|
|
req_obj->num_lines = num_lines;
|
|
req_obj->buffer = buffer;
|
|
|
|
return (PyObject *)req_obj;
|
|
}
|