diff mbox series

[ovs-dev] ovsdb: monitor: Destroy initial change set when new columns added.

Message ID 20230607130832.688251-1-i.maximets@ovn.org
State Accepted
Commit 469e98e16db1a67765bffd23d62c47b69dfa73cd
Headers show
Series [ovs-dev] ovsdb: monitor: Destroy initial change set when new columns added. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Ilya Maximets June 7, 2023, 1:08 p.m. UTC
Initial change set is preserved for as long as the monitor itself.
However, if a new client has a condition on a column that is not
one of the monitored columns, this column will be added to the
monitor via ovsdb_monitor_condition_bind().  This new column, however,
doesn't exist in the initial change set.  That will cause ovsdb-server
to malfunction or crash trying to access non-existent column during
condition evaluation:

 ERROR: AddressSanitizer: heap-buffer-overflow
 READ of size 4 at 0x606000006780 thread T0
     0 ovsdb_clause_evaluate ovsdb/condition.c:328:26
     1 ovsdb_condition_match_any_clause ovsdb/condition.c:441:13
     2 ovsdb_condition_empty_or_match_any ovsdb/condition.h:84:13
     3 ovsdb_monitor_row_update_type_condition ovsdb/monitor.c:892:28
     4 ovsdb_monitor_compose_row_update2 ovsdb/monitor.c:1058:12
     5 ovsdb_monitor_compose_update ovsdb/monitor.c:1172:24
     6 ovsdb_monitor_get_update ovsdb/monitor.c:1276:24
     7 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1505:12
     8 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
     9 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
    10 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
    11 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
    12 main_loop ovsdb/ovsdb-server.c:222:9
    13 main ovsdb/ovsdb-server.c:500:5
    14 __libc_start_call_main
    15 __libc_start_main@GLIBC_2.2.5
    16 _start (ovsdb/ovsdb-server+0x473034)

 Located 0 bytes after 64-byte region [0x606000006740,0x606000006780)
 allocated by thread T0 here:
     0 malloc (ovsdb/ovsdb-server+0x50dc82)
     1 xmalloc__ lib/util.c:140:15
     2 xmalloc lib/util.c:175:12
     3 clone_monitor_row_data ovsdb/monitor.c:336:12
     4 ovsdb_monitor_changes_update ovsdb/monitor.c:1384:23
     5 ovsdb_monitor_get_initial ovsdb/monitor.c:1535:21
     6 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1502:9
     7 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
     8 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
     9 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
    10 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
    11 main_loop ovsdb/ovsdb-server.c:222:9
    12 main ovsdb/ovsdb-server.c:500:5
    13 __libc_start_call_main
    14 __libc_start_main@GLIBC_2.2.5
    15 _start (ovsdb/ovsdb-server+0x473034)

Fix that by destroying the initial change set every time new columns
are added to the monitor.  This will trigger re-generation of the
change set and it will contain all the necessary columns afterwards.

Fixes: 07c27226ee96 ("ovsdb: Monitor: Keep and maintain the initial change set.")
Reported-by: Han Zhou <hzhou@ovn.org>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
---
 ovsdb/monitor.c        | 15 +++++++++-
 tests/ovsdb-monitor.at | 66 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)

Comments

Han Zhou June 8, 2023, 5:34 a.m. UTC | #1
On Wed, Jun 7, 2023 at 6:07 AM Ilya Maximets <i.maximets@ovn.org> wrote:
>
> Initial change set is preserved for as long as the monitor itself.
> However, if a new client has a condition on a column that is not
> one of the monitored columns, this column will be added to the
> monitor via ovsdb_monitor_condition_bind().  This new column, however,
> doesn't exist in the initial change set.  That will cause ovsdb-server
> to malfunction or crash trying to access non-existent column during
> condition evaluation:
>
>  ERROR: AddressSanitizer: heap-buffer-overflow
>  READ of size 4 at 0x606000006780 thread T0
>      0 ovsdb_clause_evaluate ovsdb/condition.c:328:26
>      1 ovsdb_condition_match_any_clause ovsdb/condition.c:441:13
>      2 ovsdb_condition_empty_or_match_any ovsdb/condition.h:84:13
>      3 ovsdb_monitor_row_update_type_condition ovsdb/monitor.c:892:28
>      4 ovsdb_monitor_compose_row_update2 ovsdb/monitor.c:1058:12
>      5 ovsdb_monitor_compose_update ovsdb/monitor.c:1172:24
>      6 ovsdb_monitor_get_update ovsdb/monitor.c:1276:24
>      7 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1505:12
>      8 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
>      9 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
>     10 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
>     11 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
>     12 main_loop ovsdb/ovsdb-server.c:222:9
>     13 main ovsdb/ovsdb-server.c:500:5
>     14 __libc_start_call_main
>     15 __libc_start_main@GLIBC_2.2.5
>     16 _start (ovsdb/ovsdb-server+0x473034)
>
>  Located 0 bytes after 64-byte region [0x606000006740,0x606000006780)
>  allocated by thread T0 here:
>      0 malloc (ovsdb/ovsdb-server+0x50dc82)
>      1 xmalloc__ lib/util.c:140:15
>      2 xmalloc lib/util.c:175:12
>      3 clone_monitor_row_data ovsdb/monitor.c:336:12
>      4 ovsdb_monitor_changes_update ovsdb/monitor.c:1384:23
>      5 ovsdb_monitor_get_initial ovsdb/monitor.c:1535:21
>      6 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1502:9
>      7 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
>      8 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
>      9 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
>     10 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
>     11 main_loop ovsdb/ovsdb-server.c:222:9
>     12 main ovsdb/ovsdb-server.c:500:5
>     13 __libc_start_call_main
>     14 __libc_start_main@GLIBC_2.2.5
>     15 _start (ovsdb/ovsdb-server+0x473034)
>
> Fix that by destroying the initial change set every time new columns
> are added to the monitor.  This will trigger re-generation of the
> change set and it will contain all the necessary columns afterwards.
>
> Fixes: 07c27226ee96 ("ovsdb: Monitor: Keep and maintain the initial
change set.")
> Reported-by: Han Zhou <hzhou@ovn.org>
> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
> ---
>  ovsdb/monitor.c        | 15 +++++++++-
>  tests/ovsdb-monitor.at | 66 ++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 80 insertions(+), 1 deletion(-)
>
> diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
> index 04dcd2298..4afaa89f4 100644
> --- a/ovsdb/monitor.c
> +++ b/ovsdb/monitor.c
> @@ -478,6 +478,7 @@ ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon,
>                           enum ovsdb_monitor_selection select,
>                           bool monitored)
>  {
> +    struct ovsdb_monitor_change_set *mcs;
>      struct ovsdb_monitor_table *mt;
>      struct ovsdb_monitor_column *c;
>
> @@ -488,6 +489,18 @@ ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon,
>          return column->name;
>      }
>
> +    mcs = dbmon->init_change_set;
> +    if (mcs) {
> +        /* A new column is going to be added to the monitor.  Existing
> +         * initial change set doesn't have it, so can no longer be used.
> +         * Initial change set is never used by more than one session at
> +         * the same time, so it's safe to destroy it here. */
> +        ovs_assert(mcs->n_refs == 1);
> +        ovsdb_monitor_json_cache_destroy(dbmon, mcs);
> +        ovsdb_monitor_change_set_destroy(mcs);
> +        dbmon->init_change_set = NULL;
> +    }
> +
>      if (mt->n_columns >= mt->allocated_columns) {
>          mt->columns = x2nrealloc(mt->columns, &mt->allocated_columns,
>                                   sizeof *mt->columns);
> @@ -614,7 +627,7 @@ ovsdb_monitor_untrack_change_set(struct ovsdb_monitor
*dbmon,
>      if (--mcs->n_refs == 0) {
>          if (mcs == dbmon->init_change_set) {
>              /* The initial change set should exist as long as the
> -             * monitor itself. */
> +             * monitor doesn't change. */
>              mcs->n_refs++;
>              return;
>          } else if (mcs == dbmon->new_change_set) {
> diff --git a/tests/ovsdb-monitor.at b/tests/ovsdb-monitor.at
> index 7e1ff64f0..12cd2bc31 100644
> --- a/tests/ovsdb-monitor.at
> +++ b/tests/ovsdb-monitor.at
> @@ -1011,3 +1011,69 @@ row,action,name,number,_version
>  ]], [ignore])
>  AT_CLEANUP
>
> +AT_SETUP([monitor-cond initial reply with condition on non-monitored
column])
> +AT_KEYWORDS([ovsdb server monitor monitor-cond positive initial
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])
> +
> +dnl Initialize the database content.
> +for txn in m4_foreach([txn], [[[["ordinals",
> +      {"op": "insert",
> +       "table": "ordinals",
> +       "row": {"number": 0, "name": "zero"}},
> +      {"op": "insert",
> +       "table": "ordinals",
> +       "row": {"number": 1, "name": "one"}},
> +      {"op": "insert",
> +       "table": "ordinals",
> +       "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do
> +  AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore],
[ignore])
> +done
> +
> +dnl Start a first client that monitors only the column 'name'.
> +on_exit 'kill `cat client-1.pid`'
> +AT_CAPTURE_FILE([client-1.out])
> +AT_CHECK([ovsdb-client -vjsonrpc --pidfile=client-1.pid --detach
--no-chdir \
> +            -d json monitor-cond --format=csv unix:socket \
> +            ordinals '[[true]]' ordinals ["name"] \
> +            > client-1.out 2> client-1.err], [0], [ignore], [ignore])
> +dnl Wait for the initial monitor reply.
> +OVS_WAIT_UNTIL([grep -q 'initial' client-1.out])
> +
> +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_CHECK([ovsdb-client -vjsonrpc --pidfile=client-2.pid --detach
--no-chdir \
> +            -d json monitor-cond --format=csv unix:socket \
> +            ordinals '[[["number", "!=", 1]]]' ordinals ["name"] \
> +            > client-2.out 2> client-2.err], [0], [ignore], [ignore])
> +dnl Wait for the initial monitor reply.
> +OVS_WAIT_UNTIL([grep -q 'initial' client-2.out])
> +
> +OVSDB_SERVER_SHUTDOWN
> +OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && \
> +                    test ! -e client-1.pid && test ! -e client-2.pid])
> +
> +dnl The first client should have all the names.
> +AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < client-1.out |
uuidfilt],
> +         [0], [dnl
> +row,action,name
> +<0>,initial,"""one"""
> +<1>,initial,"""two"""
> +<2>,initial,"""zero"""
> +])
> +
> +dnl The second client should not have the name 'one'.
> +AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < client-2.out |
uuidfilt],
> +         [0], [dnl
> +row,action,name
> +<0>,initial,"""two"""
> +<1>,initial,"""zero"""
> +])
> +AT_CLEANUP
> --
> 2.40.1
>

Thanks Ilya.
Acked-by: Han Zhou <hzhou@ovn.org>
Simon Horman June 8, 2023, 2:22 p.m. UTC | #2
On Wed, Jun 07, 2023 at 03:08:32PM +0200, Ilya Maximets wrote:
> Initial change set is preserved for as long as the monitor itself.
> However, if a new client has a condition on a column that is not
> one of the monitored columns, this column will be added to the
> monitor via ovsdb_monitor_condition_bind().  This new column, however,
> doesn't exist in the initial change set.  That will cause ovsdb-server
> to malfunction or crash trying to access non-existent column during
> condition evaluation:
> 
>  ERROR: AddressSanitizer: heap-buffer-overflow
>  READ of size 4 at 0x606000006780 thread T0
>      0 ovsdb_clause_evaluate ovsdb/condition.c:328:26
>      1 ovsdb_condition_match_any_clause ovsdb/condition.c:441:13
>      2 ovsdb_condition_empty_or_match_any ovsdb/condition.h:84:13
>      3 ovsdb_monitor_row_update_type_condition ovsdb/monitor.c:892:28
>      4 ovsdb_monitor_compose_row_update2 ovsdb/monitor.c:1058:12
>      5 ovsdb_monitor_compose_update ovsdb/monitor.c:1172:24
>      6 ovsdb_monitor_get_update ovsdb/monitor.c:1276:24
>      7 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1505:12
>      8 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
>      9 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
>     10 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
>     11 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
>     12 main_loop ovsdb/ovsdb-server.c:222:9
>     13 main ovsdb/ovsdb-server.c:500:5
>     14 __libc_start_call_main
>     15 __libc_start_main@GLIBC_2.2.5
>     16 _start (ovsdb/ovsdb-server+0x473034)
> 
>  Located 0 bytes after 64-byte region [0x606000006740,0x606000006780)
>  allocated by thread T0 here:
>      0 malloc (ovsdb/ovsdb-server+0x50dc82)
>      1 xmalloc__ lib/util.c:140:15
>      2 xmalloc lib/util.c:175:12
>      3 clone_monitor_row_data ovsdb/monitor.c:336:12
>      4 ovsdb_monitor_changes_update ovsdb/monitor.c:1384:23
>      5 ovsdb_monitor_get_initial ovsdb/monitor.c:1535:21
>      6 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1502:9
>      7 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
>      8 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
>      9 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
>     10 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
>     11 main_loop ovsdb/ovsdb-server.c:222:9
>     12 main ovsdb/ovsdb-server.c:500:5
>     13 __libc_start_call_main
>     14 __libc_start_main@GLIBC_2.2.5
>     15 _start (ovsdb/ovsdb-server+0x473034)
> 
> Fix that by destroying the initial change set every time new columns
> are added to the monitor.  This will trigger re-generation of the
> change set and it will contain all the necessary columns afterwards.
> 
> Fixes: 07c27226ee96 ("ovsdb: Monitor: Keep and maintain the initial change set.")
> Reported-by: Han Zhou <hzhou@ovn.org>
> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>

Reviewed-by: Simon Horman <simon.horman@corigine.com>
Ilya Maximets June 9, 2023, 2:27 p.m. UTC | #3
On 6/8/23 16:22, Simon Horman wrote:
> On Wed, Jun 07, 2023 at 03:08:32PM +0200, Ilya Maximets wrote:
>> Initial change set is preserved for as long as the monitor itself.
>> However, if a new client has a condition on a column that is not
>> one of the monitored columns, this column will be added to the
>> monitor via ovsdb_monitor_condition_bind().  This new column, however,
>> doesn't exist in the initial change set.  That will cause ovsdb-server
>> to malfunction or crash trying to access non-existent column during
>> condition evaluation:
>>
>>  ERROR: AddressSanitizer: heap-buffer-overflow
>>  READ of size 4 at 0x606000006780 thread T0
>>      0 ovsdb_clause_evaluate ovsdb/condition.c:328:26
>>      1 ovsdb_condition_match_any_clause ovsdb/condition.c:441:13
>>      2 ovsdb_condition_empty_or_match_any ovsdb/condition.h:84:13
>>      3 ovsdb_monitor_row_update_type_condition ovsdb/monitor.c:892:28
>>      4 ovsdb_monitor_compose_row_update2 ovsdb/monitor.c:1058:12
>>      5 ovsdb_monitor_compose_update ovsdb/monitor.c:1172:24
>>      6 ovsdb_monitor_get_update ovsdb/monitor.c:1276:24
>>      7 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1505:12
>>      8 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
>>      9 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
>>     10 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
>>     11 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
>>     12 main_loop ovsdb/ovsdb-server.c:222:9
>>     13 main ovsdb/ovsdb-server.c:500:5
>>     14 __libc_start_call_main
>>     15 __libc_start_main@GLIBC_2.2.5
>>     16 _start (ovsdb/ovsdb-server+0x473034)
>>
>>  Located 0 bytes after 64-byte region [0x606000006740,0x606000006780)
>>  allocated by thread T0 here:
>>      0 malloc (ovsdb/ovsdb-server+0x50dc82)
>>      1 xmalloc__ lib/util.c:140:15
>>      2 xmalloc lib/util.c:175:12
>>      3 clone_monitor_row_data ovsdb/monitor.c:336:12
>>      4 ovsdb_monitor_changes_update ovsdb/monitor.c:1384:23
>>      5 ovsdb_monitor_get_initial ovsdb/monitor.c:1535:21
>>      6 ovsdb_jsonrpc_monitor_create ovsdb/jsonrpc-server.c:1502:9
>>      7 ovsdb_jsonrpc_session_got_request ovsdb/jsonrpc-server.c:1030:21
>>      8 ovsdb_jsonrpc_session_run ovsdb/jsonrpc-server.c:572:17
>>      9 ovsdb_jsonrpc_session_run_all ovsdb/jsonrpc-server.c:602:21
>>     10 ovsdb_jsonrpc_server_run ovsdb/jsonrpc-server.c:417:9
>>     11 main_loop ovsdb/ovsdb-server.c:222:9
>>     12 main ovsdb/ovsdb-server.c:500:5
>>     13 __libc_start_call_main
>>     14 __libc_start_main@GLIBC_2.2.5
>>     15 _start (ovsdb/ovsdb-server+0x473034)
>>
>> Fix that by destroying the initial change set every time new columns
>> are added to the monitor.  This will trigger re-generation of the
>> change set and it will contain all the necessary columns afterwards.
>>
>> Fixes: 07c27226ee96 ("ovsdb: Monitor: Keep and maintain the initial change set.")
>> Reported-by: Han Zhou <hzhou@ovn.org>
>> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
> 
> Reviewed-by: Simon Horman <simon.horman@corigine.com>
> 

Thanks, Han and Simon!  Applied.

Best regards, Ilya Maximets.
diff mbox series

Patch

diff --git a/ovsdb/monitor.c b/ovsdb/monitor.c
index 04dcd2298..4afaa89f4 100644
--- a/ovsdb/monitor.c
+++ b/ovsdb/monitor.c
@@ -478,6 +478,7 @@  ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon,
                          enum ovsdb_monitor_selection select,
                          bool monitored)
 {
+    struct ovsdb_monitor_change_set *mcs;
     struct ovsdb_monitor_table *mt;
     struct ovsdb_monitor_column *c;
 
@@ -488,6 +489,18 @@  ovsdb_monitor_add_column(struct ovsdb_monitor *dbmon,
         return column->name;
     }
 
+    mcs = dbmon->init_change_set;
+    if (mcs) {
+        /* A new column is going to be added to the monitor.  Existing
+         * initial change set doesn't have it, so can no longer be used.
+         * Initial change set is never used by more than one session at
+         * the same time, so it's safe to destroy it here. */
+        ovs_assert(mcs->n_refs == 1);
+        ovsdb_monitor_json_cache_destroy(dbmon, mcs);
+        ovsdb_monitor_change_set_destroy(mcs);
+        dbmon->init_change_set = NULL;
+    }
+
     if (mt->n_columns >= mt->allocated_columns) {
         mt->columns = x2nrealloc(mt->columns, &mt->allocated_columns,
                                  sizeof *mt->columns);
@@ -614,7 +627,7 @@  ovsdb_monitor_untrack_change_set(struct ovsdb_monitor *dbmon,
     if (--mcs->n_refs == 0) {
         if (mcs == dbmon->init_change_set) {
             /* The initial change set should exist as long as the
-             * monitor itself. */
+             * monitor doesn't change. */
             mcs->n_refs++;
             return;
         } else if (mcs == dbmon->new_change_set) {
diff --git a/tests/ovsdb-monitor.at b/tests/ovsdb-monitor.at
index 7e1ff64f0..12cd2bc31 100644
--- a/tests/ovsdb-monitor.at
+++ b/tests/ovsdb-monitor.at
@@ -1011,3 +1011,69 @@  row,action,name,number,_version
 ]], [ignore])
 AT_CLEANUP
 
+AT_SETUP([monitor-cond initial reply with condition on non-monitored column])
+AT_KEYWORDS([ovsdb server monitor monitor-cond positive initial 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])
+
+dnl Initialize the database content.
+for txn in m4_foreach([txn], [[[["ordinals",
+      {"op": "insert",
+       "table": "ordinals",
+       "row": {"number": 0, "name": "zero"}},
+      {"op": "insert",
+       "table": "ordinals",
+       "row": {"number": 1, "name": "one"}},
+      {"op": "insert",
+       "table": "ordinals",
+       "row": {"number": 2, "name": "two"}}]]]], ['txn' ]); do
+  AT_CHECK([ovsdb-client transact unix:socket "$txn"], [0], [ignore], [ignore])
+done
+
+dnl Start a first client that monitors only the column 'name'.
+on_exit 'kill `cat client-1.pid`'
+AT_CAPTURE_FILE([client-1.out])
+AT_CHECK([ovsdb-client -vjsonrpc --pidfile=client-1.pid --detach --no-chdir \
+            -d json monitor-cond --format=csv unix:socket \
+            ordinals '[[true]]' ordinals ["name"] \
+            > client-1.out 2> client-1.err], [0], [ignore], [ignore])
+dnl Wait for the initial monitor reply.
+OVS_WAIT_UNTIL([grep -q 'initial' client-1.out])
+
+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_CHECK([ovsdb-client -vjsonrpc --pidfile=client-2.pid --detach --no-chdir \
+            -d json monitor-cond --format=csv unix:socket \
+            ordinals '[[["number", "!=", 1]]]' ordinals ["name"] \
+            > client-2.out 2> client-2.err], [0], [ignore], [ignore])
+dnl Wait for the initial monitor reply.
+OVS_WAIT_UNTIL([grep -q 'initial' client-2.out])
+
+OVSDB_SERVER_SHUTDOWN
+OVS_WAIT_UNTIL([test ! -e ovsdb-server.pid && \
+                    test ! -e client-1.pid && test ! -e client-2.pid])
+
+dnl The first client should have all the names.
+AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < client-1.out | uuidfilt],
+         [0], [dnl
+row,action,name
+<0>,initial,"""one"""
+<1>,initial,"""two"""
+<2>,initial,"""zero"""
+])
+
+dnl The second client should not have the name 'one'.
+AT_CHECK([$PYTHON3 $srcdir/ovsdb-monitor-sort.py < client-2.out | uuidfilt],
+         [0], [dnl
+row,action,name
+<0>,initial,"""two"""
+<1>,initial,"""zero"""
+])
+AT_CLEANUP