TCF Agent: improved checks for invalid of defunct register IDs
diff --git a/agent/tcf/framework/cpudefs.c b/agent/tcf/framework/cpudefs.c
index 8db5a72..a2c1ade 100644
--- a/agent/tcf/framework/cpudefs.c
+++ b/agent/tcf/framework/cpudefs.c
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007-2020 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2021 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.
@@ -33,6 +33,15 @@
 
 #include <tcf/framework/cpudefs-ext.h>
 
+typedef struct ContextExtensionRegisters {
+    unsigned reg_seq;
+    unsigned reg_cnt;
+} ContextExtensionRegisters;
+
+static unsigned reg_defs_seq = 1;
+static size_t context_extension_offset = 0;
+#define EXT(ctx) ((ContextExtensionRegisters *)((char *)(ctx) + context_extension_offset))
+
 void create_reg_children_refs(RegisterDefinition * defs) {
 #if ENABLE_RegisterChildrenRefs
     RegisterDefinition * r = NULL;
@@ -196,10 +205,27 @@
     return tmp_printf("FP%d.%s", frame, ctx->id);
 }
 
+static void update_reg_cnt(Context * ctx, RegisterDefinition * defs) {
+    ContextExtensionRegisters * ext = EXT(ctx);
+    assert(reg_defs_seq != 0);
+    if (ext->reg_seq != reg_defs_seq) {
+        ext->reg_cnt = 0;
+        if (defs != NULL) {
+            while (defs->name != NULL) {
+                ext->reg_cnt++;
+                defs++;
+            }
+        }
+        ext->reg_seq = reg_defs_seq;
+    }
+}
+
 const char * register2id(Context * ctx, int frame, RegisterDefinition * reg) {
     RegisterDefinition * defs = get_reg_definitions(ctx);
+    update_reg_cnt(ctx, defs);
     assert(defs != NULL);
     assert(reg >= defs);
+    assert(reg < defs + EXT(ctx)->reg_cnt);
     if (frame < 0) return tmp_printf("R%d.%s", (int)(reg - defs), ctx->id);
     return tmp_printf("R%d@%d.%s", (int)(reg - defs), frame, ctx->id);
 }
@@ -209,7 +235,7 @@
     *frame = STACK_TOP_FRAME;
     *reg_num = 0;
     if (*id++ != 'R') {
-        errno = ERR_INV_CONTEXT;
+        errno = ERR_OTHER;
         return -1;
     }
     while (*id != '.' && *id != '@') {
@@ -217,7 +243,7 @@
             *reg_num = *reg_num * 10 + (*id++ - '0');
         }
         else {
-            errno = ERR_INV_CONTEXT;
+            errno = ERR_OTHER;
             return -1;
         }
     }
@@ -229,7 +255,7 @@
                 n = n * 10 + (*id++ - '0');
             }
             else {
-                errno = ERR_INV_CONTEXT;
+                errno = ERR_OTHER;
                 return -1;
             }
         }
@@ -248,7 +274,10 @@
     *ctx = NULL;
     *reg_def = NULL;
 
-    if (id2reg_num(id, &ctx_id, frame, &reg_num) < 0) return -1;
+    if (id2reg_num(id, &ctx_id, frame, &reg_num) < 0) {
+        if (errno == ERR_OTHER) set_errno(errno, "Malformed register ID");
+        return -1;
+    }
 
     *ctx = id2ctx(ctx_id);
     if (*ctx == NULL) {
@@ -264,10 +293,20 @@
         set_errno(ERR_OTHER, "Context has no registers");
         return -1;
     }
+    update_reg_cnt(*ctx, defs);
+    if (reg_num >= EXT(*ctx)->reg_cnt) {
+        set_errno(ERR_OTHER, "Invalid register ID");
+        return -1;
+    }
     *reg_def = defs + reg_num;
     return 0;
 }
 
+void invalidate_register_ids(void) {
+    do reg_defs_seq++;
+    while (reg_defs_seq == 0);
+}
+
 static void location_expression_error(void) {
     str_exception(ERR_OTHER, "Invalid location expression");
 }
@@ -739,6 +778,7 @@
 }
 
 void ini_cpudefs(void) {
+    context_extension_offset = context_extension(sizeof(ContextExtensionRegisters));
 #if defined(ENABLE_ini_cpudefs_mdep) && ENABLE_ini_cpudefs_mdep
     ini_cpudefs_mdep();
 #endif
diff --git a/agent/tcf/framework/cpudefs.h b/agent/tcf/framework/cpudefs.h
index a11a134..7289c4e 100644
--- a/agent/tcf/framework/cpudefs.h
+++ b/agent/tcf/framework/cpudefs.h
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007-2019 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2021 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.
@@ -282,7 +282,7 @@
 #endif
 
 /* Get instruction pointer (PC) value.
-* Deprecated: use get_PC() */
+ * Deprecated: use get_PC() */
 extern ContextAddress get_regs_PC(Context * ctx);
 
 /* Set instruction pointer (PC) value.
@@ -308,6 +308,10 @@
 extern int id2reg_num(const char * id, const char ** ctx_id, int * frame, unsigned * reg_num);
 extern int id2register(const char * id, Context ** ctx, int * frame, RegisterDefinition ** reg_def);
 
+/* Invalidate register IDs.
+ * This function is called by the RunControl service when register definitions change. */
+extern void invalidate_register_ids(void);
+
 /* Get breakpoint instruction code and size.
  * Return NULL if the context does not support software breakpoints. */
 extern uint8_t * get_break_instruction(Context * ctx, size_t * size);
diff --git a/agent/tcf/services/registers.c b/agent/tcf/services/registers.c
index 9afd7a6..40dcaa0 100644
--- a/agent/tcf/services/registers.c
+++ b/agent/tcf/services/registers.c
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007-2020 Wind River Systems, Inc. and others.
+ * Copyright (c) 2007-2021 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.
@@ -442,6 +442,7 @@
 }
 
 void send_event_register_definitions_changed(void) {
+    invalidate_register_ids();
     notify_definitions_changed = 1;
     /* Delay notifications until cache transaction is committed */
     if (cache_transaction_id() == 0) flush_notifications();