TCF Agent: improved implementation of watchpoints
diff --git a/agent/tcf/services/breakpoints.c b/agent/tcf/services/breakpoints.c
index ac28c36..61a0713 100644
--- a/agent/tcf/services/breakpoints.c
+++ b/agent/tcf/services/breakpoints.c
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007-2020 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2023 Wind River Systems, Inc. and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * and Eclipse Distribution License v1.0 which accompany this distribution.
@@ -54,6 +54,8 @@
 #  define ENABLE_SkipPrologueWhenPlanting 0
 #endif
 
+#define MAX_WP_SIZE 0x10000
+
 typedef struct BreakpointRef BreakpointRef;
 typedef struct InstructionRef InstructionRef;
 typedef struct BreakInstruction BreakInstruction;
@@ -287,11 +289,13 @@
 }
 
 static unsigned get_bp_access_types(BreakpointInfo * bp, int virtual_addr) {
+    /* Return breakpoint instruction access types for given breakpoint */
     char * type = bp->type;
     unsigned access_types = bp->access_mode;
     if (access_types == 0 && (bp->file != NULL || bp->location != NULL)) access_types |= CTX_BP_ACCESS_INSTRUCTION;
     if (virtual_addr && type != NULL && strcmp(type, "Software") == 0) access_types |= CTX_BP_ACCESS_SOFTWARE;
     if (virtual_addr) access_types |= CTX_BP_ACCESS_VIRTUAL;
+    access_types &= ~CTX_BP_ACCESS_CHANGE; /* Handled by the service */
     return access_types;
 }
 
@@ -1650,6 +1654,7 @@
     unsigned bit_offs = 0;
     unsigned bit_size = 0;
     void * bit_value = NULL;
+    int read_bit_value = 0;
     int error = 0;
 #if ENABLE_Expressions
     Value v;
@@ -1697,8 +1702,7 @@
         bit_offs += (unsigned)(addr & (size - 1)) * 8;
         addr &= ~(size - 1);
         if ((bp->access_mode & CTX_BP_ACCESS_DATA_READ) == 0) {
-            bit_value = tmp_alloc_zero(size);
-            if (!error && context_read_mem(ctx, addr, bit_value, size) < 0) error = errno;
+            read_bit_value = 1;
         }
     }
     else if (bp->access_mode & (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE)) {
@@ -1716,6 +1720,19 @@
         }
 #endif
     }
+    if ((bp->access_mode & (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE)) != 0 && (bp->access_mode & CTX_BP_ACCESS_CHANGE) != 0) {
+        read_bit_value = 1;
+    }
+    if (!error && size == 0) error = set_errno(ERR_OTHER, "Invalid breakpoint size: 0");
+    if (!error && read_bit_value) {
+        if (size > MAX_WP_SIZE) {
+            error = set_fmt_errno(ERR_BUFFER_OVERFLOW, "Too large watchpoint size: > 0x%x", MAX_WP_SIZE);
+        }
+        else {
+            bit_value = tmp_alloc_zero(size);
+            if (!error && context_read_mem(ctx, addr, bit_value, size) < 0) error = errno;
+        }
+    }
     if (error) {
         address_expression_error(ctx, bp, error);
     }
@@ -1925,14 +1942,19 @@
                 error = errno;
             }
             else {
-                unsigned i;
                 int changed = 0;
-                for (i = bi->bit_offs; i < bi->bit_offs + bi->bit_size; i++) {
-                    uint8_t v0 = ((uint8_t *)bi->bit_value)[i / 8];
-                    uint8_t v1 = ((uint8_t *)bi->bit_next)[i / 8];
-                    if ((v0 & (1 << (i % 8))) != (v1 & (1 << (i % 8)))) {
-                        changed = 1;
-                        break;
+                if (bi->bit_size == 0) {
+                    changed = memcmp(bi->bit_value, bi->bit_next, size) != 0;
+                }
+                else {
+                    unsigned i;
+                    for (i = bi->bit_offs; i < bi->bit_offs + bi->bit_size; i++) {
+                        uint8_t v0 = ((uint8_t *)bi->bit_value)[i / 8];
+                        uint8_t v1 = ((uint8_t *)bi->bit_next)[i / 8];
+                        if ((v0 & (1 << (i % 8))) != (v1 & (1 << (i % 8)))) {
+                            changed = 1;
+                            break;
+                        }
                     }
                 }
                 if (!changed) condition_ok = 0;