From: Alejandro Pacheco <[email protected]>
ovsdb_monitor_compose_row_update() checks the changed columns of a row
using the current number of columns of the monitor table instead of the
number of columns of the change set the row belongs to. The monitor
table gains columns when another session adds a condition on a column
that is not monitored, so for a session that still has older changes
pending, the check reads past the end of the row data.
Use the number of columns of the change set, as the update2 version
already does.
Fixes: e0f42d4a6548 ("monitor: Fix crash when monitor condition adds new
columns.")
Assisted-by: claude-opus-5-5, Claude Code
Signed-off-by: Alejandro Pacheco <[email protected]>
---
ovsdb/monitor.c | 4 +--
tests/ovsdb-monitor.at | 75 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
index 7e94e6bad..3963d1ced 100644
--- a/ovsdb/monitor.c
+++ b/ovsdb/monitor.c
@@ -999,7 +999,7 @@ ovsdb_monitor_compose_row_update(
enum ovsdb_monitor_row_type row_type OVS_UNUSED,
const void *_row,
bool initial, unsigned long int *changed,
- size_t n_columns OVS_UNUSED)
+ size_t n_columns)
{
const struct ovsdb_monitor_row *row = _row;
enum ovsdb_monitor_selection type;
@@ -1011,7 +1011,7 @@ ovsdb_monitor_compose_row_update(
type = ovsdb_monitor_row_update_type(initial, row->old, row->new);
if (ovsdb_monitor_row_skip_update(mt, row_type, row->old,
row->new, type, changed,
- mt->n_columns)) {
+ n_columns)) {
return NULL;
}
diff --git a/tests/ovsdb-monitor.at b/tests/ovsdb-monitor.at
index c58fb5fcf..31b9a19d6 100644
--- a/tests/ovsdb-monitor.at
+++ b/tests/ovsdb-monitor.at
@@ -1213,3 +1213,78 @@ row,action,name
<1>,insert,"""one"""
])
AT_CLEANUP
+
+AT_SETUP([monitor with pending updates and condition on non-monitored column])
+AT_KEYWORDS([ovsdb server monitor positive non-monitored])
+
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
+on_exit 'kill `cat ovsdb-server.pid`'
+AT_CAPTURE_FILE([ovsdb-server.log])
+AT_CHECK([ovsdb-server --detach --no-chdir --pidfile \
+ --remote=punix:socket --log-file db], [0], [ignore], [ignore])
+
+AT_CHECK([ovsdb-client transact unix:socket '[["ordinals",
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 0, "name": "zero"}}]]'], [0], [ignore], [ignore])
+
+dnl Start a first client that monitors only the column 'name'.
+on_exit 'kill -CONT `cat client-1.pid`; kill `cat client-1.pid`'
+AT_CAPTURE_FILE([client-1.out])
+AT_CAPTURE_FILE([client-1.err])
+AT_CHECK([ovsdb-client -vjsonrpc --pidfile=client-1.pid --detach --no-chdir \
+ -d json monitor --format=csv unix:socket ordinals ordinals name \
+ > client-1.out 2> client-1.err], [0], [ignore], [ignore])
+OVS_WAIT_UNTIL([grep -q 'initial' client-1.out])
+
+dnl Stop the first client and send it more than its socket can buffer, so
+dnl that the server keeps its next changes pending.
+AT_CHECK([kill -STOP `cat client-1.pid`])
+get_backlog () {
+ ovs-appctl -t ovsdb-server memory/show | tr ' ' '\n' dnl
+ | grep '^backlog:' | cut -d ':' -f 2
+}
+long=$(printf '%0100000d' 0)
+i=0
+while test X"$(get_backlog)" = X && test $i -lt 100; do
+ i=$((i + 1))
+ AT_CHECK([ovsdb-client transact unix:socket '[["ordinals",
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": '$((i + 100))', "name": "'$long'"}}]]'],
+ [0], [ignore], [ignore])
+done
+AT_CHECK([test X"$(get_backlog)" != X])
+
+dnl A change that stays pending for the first client.
+AT_CHECK([ovsdb-client transact unix:socket '[["ordinals",
+ {"op": "insert",
+ "table": "ordinals",
+ "row": {"number": 1, "name": "one"}}]]'], [0], [ignore], [ignore])
+
+dnl Start a second client that monitors the column 'name', but has a condition
+dnl on column 'number'.
+on_exit 'kill `cat client-2.pid`'
+AT_CAPTURE_FILE([client-2.out])
+AT_CAPTURE_FILE([client-2.err])
+AT_CHECK([ovsdb-client -vjsonrpc --pidfile=client-2.pid --detach --no-chdir \
+ -d json monitor-cond --format=csv unix:socket \
+ ordinals '[[["number", "==", 0]]]' ordinals ["name"] \
+ > client-2.out 2> client-2.err], [0], [ignore], [ignore])
+OVS_WAIT_UNTIL([grep -q 'initial' client-2.out])
+
+dnl Modify a row while the first client still has pending changes.
+AT_CHECK([ovsdb-client transact unix:socket '[["ordinals",
+ {"op": "update",
+ "table": "ordinals",
+ "where": [["number", "==", 0]],
+ "row": {"name": "ZERO"}}]]'], [0], [ignore], [ignore])
+
+AT_CHECK([kill -CONT `cat client-1.pid`])
+OVS_WAIT_UNTIL([grep -q 'ZERO' client-1.out && grep -q 'ZERO' client-2.out])
+
+OVSDB_SERVER_SHUTDOWN
+OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && \
+ test ! -e client-1.pid && test ! -e client-2.pid])
+AT_CLEANUP
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev