Hello to all,
I suspect I found a bug in the agent/helpers/table_iterator.c, related
to GET requests of several variables - it doesn't show for one variable
or for get nexts.
The summary: iinfo->free_data_context gets called more than one time for
the same data context.
Setup:
- an iterator info which has free_data_context
- make a request for N different rows in the table, by directly
specifying the index and its columns; i.e., request more than one
attribute for the same index
What's happening:
- the netsnmp_table_iterator_helper_handler goes like this, around
line 454:
index_search =
(iinfo->get_first_data_point) (&callback_loop_context,
&callback_data_context,
index_search, iinfo);
/* loop over each data point */
while(index_search) {
/* remember to free this later */
free_this_index_search = index_search;
/* compare against each request*/
for(request = requests ; request; request = request->next) {
.....
switch(reqinfo->mode) {
case MODE_GET:
case MODE_SET_RESERVE1:
if (snmp_oid_compare(myname, myname_len,
request->requestvb->name,
request->requestvb->name_length) == 0) {
.....
request_count--; /* One less to look for */
} else {
if (iinfo->free_data_context && callback_data_context) {
(iinfo->free_data_context)(callback_data_context,
iinfo);
}
}
break;
The ..... have been deleted as not of importance. Not that, for the
first data context as returned by get_first_data_point, we have N
requests to match against it. Suppose the first fails, then the
free_data_context is called against it. Then the code breaks and goes
back to the for(request =...) and comes back to switch(reqinfo->mode).
But this time, the callback_data_context has been free()'d and is
invalid.
If what I understand from the code is correct, the agent should only
free the data_context when the for(request =...) is finished, no during
the for, because we might use the same data_context more than once.
This means that any request for more rows in a table which uses
free_data_context corrupts the memory and possibly crashes the agent.
Also, at line 587 in the same function, a comparison is made between
callback_data_context and last_loop_context, which represent different
types; I think this should be callback_loop_context and
last_loop_context.
Attached is a patch which seems to correct this problem by moving the
free just before the get_next_data_point. There is one problem
remaining, one extra free() is made by netsnmp_free_ti_cache
(table_iterator.c:246), but I don't understand this code.
The tests and debugging have been made with a module of my own and
valgrind.
Regards,
Iustin Pop
--- table_iterator.c.old 2004-10-13 22:52:24.000000000 +0300
+++ table_iterator.c 2004-10-13 22:59:07.000000000 +0300
@@ -331,6 +331,7 @@
netsnmp_table_registration_info *table_reg_info = NULL;
int i;
netsnmp_data_list *ldata;
+ void *free_this_data_context;
iinfo = (netsnmp_iterator_info *) handler->myvoid;
if (!iinfo || !reginfo || !reqinfo)
@@ -453,6 +454,8 @@
/* loop over each data point */
while(index_search) {
+ free_this_data_context = NULL;
+
/* remember to free this later */
free_this_index_search = index_search;
@@ -484,10 +487,7 @@
callback_loop_context, iinfo);
request_count--; /* One less to look for */
} else {
- if (iinfo->free_data_context && callback_data_context) {
- (iinfo->free_data_context)(callback_data_context,
- iinfo);
- }
+ free_this_data_context = callback_data_context;
}
break;
@@ -552,10 +552,7 @@
request_count--;
} else {
- if (iinfo->free_data_context && callback_data_context) {
- (iinfo->free_data_context)(callback_data_context,
- iinfo);
- }
+ free_this_data_context = callback_data_context;
}
break;
@@ -574,6 +571,14 @@
}
}
+ /* We have checked all requests against this data context,
+ * and we can free it now (if required)
+ */
+ if(free_this_data_context && iinfo->free_data_context) {
+ (iinfo->free_data_context)(free_this_data_context,
+ iinfo);
+ }
+
/* Is there any point in carrying on? */
if (!request_count)
break;
@@ -584,7 +589,7 @@
&callback_data_context,
index_search, iinfo);
if (iinfo->free_loop_context && last_loop_context &&
- callback_data_context != last_loop_context) {
+ callback_loop_context != last_loop_context) {
(iinfo->free_loop_context) (last_loop_context, iinfo);
last_loop_context = NULL;
}