1
0
mirror of https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git synced 2026-10-25 12:39:18 +03:00
Files
libgpiod/bindings/python/gpiod/ext/request.c
Bartosz Golaszewski c88f678e36 bindings: python: check for errors after PyIter_Next() in request_get_values
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>
2026-06-08 10:20:00 +02:00

438 lines
9.3 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 int request_init(PyObject *Py_UNUSED(ignored0),
PyObject *Py_UNUSED(ignored1),
PyObject *Py_UNUSED(ignored2))
{
PyErr_SetString(PyExc_NotImplementedError,
"_ext.LineRequest cannot be instantiated");
return -1;
}
static void request_finalize(request_object *self)
{
if (self->request)
PyObject_CallMethod((PyObject *)self, "release", "");
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))
{
Py_BEGIN_ALLOW_THREADS;
gpiod_line_request_release(self->request);
Py_END_ALLOW_THREADS;
self->request = NULL;
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 (max_events_obj != Py_None) {
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_new = PyType_GenericNew,
.tp_init = (initproc)request_init,
.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;
}