Fix memory leaks in maxinfo (modified for 2.0 and develop-branch)

MXS-1009. This commit adds a gwbuf_free after maxinfo_execute() to
free a buffer with an sql-query after it has been processed. Also,
the parse tree in maxinfo_execute_query() is now freed. The tree_free-
function was renamed to maxinfo_tree_free, since it is now globally
available.

This commit has additional changes (in relation to the 1.4.4 branch)
to remove errors caused by differences in the html and sql-sides of
MaxInfo.
This commit is contained in:
ekorh475
2016-11-25 12:53:15 +02:00
parent 0bc68742a6
commit cc54d80a8b
4 changed files with 186 additions and 156 deletions

View File

@ -132,6 +132,7 @@ typedef enum
extern MAXINFO_TREE *maxinfo_parse(char *, PARSE_ERROR *); extern MAXINFO_TREE *maxinfo_parse(char *, PARSE_ERROR *);
extern void maxinfo_free_tree(MAXINFO_TREE *);
extern void maxinfo_execute(DCB *, MAXINFO_TREE *); extern void maxinfo_execute(DCB *, MAXINFO_TREE *);
extern void maxinfo_send_error(DCB *, int, char *); extern void maxinfo_send_error(DCB *, int, char *);
extern void maxinfo_send_parse_error(DCB *, char *, PARSE_ERROR); extern void maxinfo_send_parse_error(DCB *, char *, PARSE_ERROR);

View File

@ -335,54 +335,60 @@ static void handleError(
static int static int
execute(ROUTER *rinstance, void *router_session, GWBUF *queue) execute(ROUTER *rinstance, void *router_session, GWBUF *queue)
{ {
INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance; INFO_INSTANCE *instance = (INFO_INSTANCE *)rinstance;
INFO_SESSION *session = (INFO_SESSION *)router_session; INFO_SESSION *session = (INFO_SESSION *)router_session;
uint8_t *data; uint8_t *data;
int length, len, residual; int length, len, residual;
char *sql; char *sql;
if (GWBUF_TYPE(queue) == GWBUF_TYPE_HTTP) if (GWBUF_TYPE(queue) == GWBUF_TYPE_HTTP)
{ {
return handle_url(instance, session, queue); return handle_url(instance, session, queue);
} }
if (session->queue) if (session->queue)
{ {
queue = gwbuf_append(session->queue, queue); queue = gwbuf_append(session->queue, queue);
session->queue = NULL; session->queue = NULL;
queue = gwbuf_make_contiguous(queue); queue = gwbuf_make_contiguous(queue);
} }
data = (uint8_t *)GWBUF_DATA(queue); data = (uint8_t *)GWBUF_DATA(queue);
length = data[0] + (data[1] << 8) + (data[2] << 16); length = data[0] + (data[1] << 8) + (data[2] << 16);
if (length + 4 > GWBUF_LENGTH(queue)) if (length + 4 > GWBUF_LENGTH(queue))
{ {
// Incomplete packet, must be buffered // Incomplete packet, must be buffered
session->queue = queue; session->queue = queue;
return 1; return 1;
} }
// We have a complete request in a signle buffer int rc = 1;
if (modutil_MySQL_Query(queue, &sql, &len, &residual)) // We have a complete request in a single buffer
{ if (modutil_MySQL_Query(queue, &sql, &len, &residual))
sql = strndup(sql, len); {
int rc = maxinfo_execute_query(instance, session, sql); sql = strndup(sql, len);
free(sql); rc = maxinfo_execute_query(instance, session, sql);
return rc; free(sql);
} }
else else
{ {
switch (MYSQL_COMMAND(queue)) switch (MYSQL_COMMAND(queue))
{ {
case COM_PING: case COM_PING:
return maxinfo_ping(instance, session, queue); rc = maxinfo_ping(instance, session, queue);
case COM_STATISTICS: break;
return maxinfo_statistics(instance, session, queue); case COM_STATISTICS:
default: rc = maxinfo_statistics(instance, session, queue);
MXS_ERROR("maxinfo: Unexpected MySQL command 0x%x", break;
MYSQL_COMMAND(queue)); case COM_QUIT:
} break;
} default:
MXS_ERROR("maxinfo: Unexpected MySQL command 0x%x",
return 1; MYSQL_COMMAND(queue));
break;
}
}
// MaxInfo doesn't route the data forward so it should be freed.
gwbuf_free(queue);
return rc;
} }
/** /**
@ -601,51 +607,54 @@ uint8_t *ptr;
static int static int
maxinfo_execute_query(INFO_INSTANCE *instance, INFO_SESSION *session, char *sql) maxinfo_execute_query(INFO_INSTANCE *instance, INFO_SESSION *session, char *sql)
{ {
MAXINFO_TREE *tree; MAXINFO_TREE *tree;
PARSE_ERROR err; PARSE_ERROR err;
MXS_INFO("maxinfo: SQL statement: '%s' for 0x%p.", MXS_INFO("maxinfo: SQL statement: '%s' for 0x%p.",
sql, session->dcb); sql, session->dcb);
if (strcmp(sql, "select @@version_comment limit 1") == 0) if (strcmp(sql, "select @@version_comment limit 1") == 0)
{ {
respond_vercom(session->dcb); respond_vercom(session->dcb);
return 1; return 1;
} }
/* Below is a kludge for MonYog, if we see /* Below is a kludge for MonYog, if we see
* select unix_timestamp... as starttime * select unix_timestamp... as starttime
* just return the starttime of MaxScale * just return the starttime of MaxScale
*/ */
if (strncasecmp(sql, "select UNIX_TIMESTAMP", if (strncasecmp(sql, "select UNIX_TIMESTAMP",
strlen("select UNIX_TIMESTAMP")) == 0 strlen("select UNIX_TIMESTAMP")) == 0
&& (strstr(sql, "as starttime") != NULL || strstr(sql, "AS starttime") != NULL)) && (strstr(sql, "as starttime") != NULL || strstr(sql, "AS starttime") != NULL))
{ {
respond_starttime(session->dcb); respond_starttime(session->dcb);
return 1; return 1;
} }
if (strcasecmp(sql, "set names 'utf8'") == 0) if (strcasecmp(sql, "set names 'utf8'") == 0)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);
} }
if (strncasecmp(sql, "set session", 11) == 0) if (strncasecmp(sql, "set session", 11) == 0)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);
} }
if (strncasecmp(sql, "set autocommit", 14) == 0) if (strncasecmp(sql, "set autocommit", 14) == 0)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);
} }
if (strncasecmp(sql, "SELECT `ENGINES`.`SUPPORT`", 26) == 0) if (strncasecmp(sql, "SELECT `ENGINES`.`SUPPORT`", 26) == 0)
{ {
return maxinfo_send_ok(session->dcb); return maxinfo_send_ok(session->dcb);
} }
if ((tree = maxinfo_parse(sql, &err)) == NULL) if ((tree = maxinfo_parse(sql, &err)) == NULL)
{ {
maxinfo_send_parse_error(session->dcb, sql, err); maxinfo_send_parse_error(session->dcb, sql, err);
MXS_NOTICE("Failed to parse SQL statement: '%s'.", sql); MXS_NOTICE("Failed to parse SQL statement: '%s'.", sql);
} }
else else
maxinfo_execute(session->dcb, tree); {
return 1; maxinfo_execute(session->dcb, tree);
maxinfo_free_tree(tree);
}
return 1;
} }
/** /**

View File

@ -881,22 +881,28 @@ variable_row(RESULTSET *result, void *data)
static void static void
exec_show_variables(DCB *dcb, MAXINFO_TREE *filter) exec_show_variables(DCB *dcb, MAXINFO_TREE *filter)
{ {
RESULTSET *result; RESULTSET *result;
VARCONTEXT context; VARCONTEXT *context;
if ((context = malloc(sizeof(VARCONTEXT))) == NULL)
{
return;
}
if (filter) if (filter)
{ {
context.like = filter->value; context->like = filter->value;
} }
else else
{ {
context.like = NULL; context->like = NULL;
} }
context.index = 0; context->index = 0;
if ((result = resultset_create(variable_row, &context)) == NULL) if ((result = resultset_create(variable_row, context)) == NULL)
{ {
maxinfo_send_error(dcb, 0, "No resources available"); maxinfo_send_error(dcb, 0, "No resources available");
free(context);
return; return;
} }
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR); resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
@ -1167,22 +1173,28 @@ status_row(RESULTSET *result, void *data)
static void static void
exec_show_status(DCB *dcb, MAXINFO_TREE *filter) exec_show_status(DCB *dcb, MAXINFO_TREE *filter)
{ {
RESULTSET *result; RESULTSET *result;
VARCONTEXT context; VARCONTEXT *context;
if ((context = malloc(sizeof(VARCONTEXT))) == NULL)
{
return;
}
if (filter) if (filter)
{ {
context.like = filter->value; context->like = filter->value;
} }
else else
{ {
context.like = NULL; context->like = NULL;
} }
context.index = 0; context->index = 0;
if ((result = resultset_create(status_row, &context)) == NULL) if ((result = resultset_create(status_row, context)) == NULL)
{ {
maxinfo_send_error(dcb, 0, "No resources available"); maxinfo_send_error(dcb, 0, "No resources available");
free(context);
return; return;
} }
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR); resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);

View File

@ -41,7 +41,7 @@
#include <log_manager.h> #include <log_manager.h>
static MAXINFO_TREE *make_tree_node(MAXINFO_OPERATOR, char *, MAXINFO_TREE *, MAXINFO_TREE *); static MAXINFO_TREE *make_tree_node(MAXINFO_OPERATOR, char *, MAXINFO_TREE *, MAXINFO_TREE *);
static void free_tree(MAXINFO_TREE *); void maxinfo_free_tree(MAXINFO_TREE *); // This function is needed by maxinfo.c
static char *fetch_token(char *, int *, char **); static char *fetch_token(char *, int *, char **);
static MAXINFO_TREE *parse_column_list(char **sql); static MAXINFO_TREE *parse_column_list(char **sql);
static MAXINFO_TREE *parse_table_name(char **sql); static MAXINFO_TREE *parse_table_name(char **sql);
@ -62,44 +62,46 @@ char *ptr, *text;
MAXINFO_TREE *tree = NULL; MAXINFO_TREE *tree = NULL;
MAXINFO_TREE *col, *table; MAXINFO_TREE *col, *table;
*parse_error = PARSE_NOERROR; *parse_error = PARSE_NOERROR;
while ((ptr = fetch_token(sql, &token, &text)) != NULL) while ((ptr = fetch_token(sql, &token, &text)) != NULL)
{ {
switch (token) switch (token)
{ {
case LT_SHOW: case LT_SHOW:
free(text); // not needed free(text); // not needed
ptr = fetch_token(ptr, &token, &text); ptr = fetch_token(ptr, &token, &text);
if (ptr == NULL || token != LT_STRING) if (ptr == NULL || token != LT_STRING)
{ {
// Expected show "name" // Expected show "name"
*parse_error = PARSE_MALFORMED_SHOW; *parse_error = PARSE_MALFORMED_SHOW;
return NULL; return NULL;
} }
tree = make_tree_node(MAXOP_SHOW, text, NULL, NULL); tree = make_tree_node(MAXOP_SHOW, text, NULL, NULL);
if ((ptr = fetch_token(ptr, &token, &text)) == NULL) if ((ptr = fetch_token(ptr, &token, &text)) == NULL)
return tree; {
else if (token == LT_LIKE) return tree;
{ }
if ((ptr = fetch_token(ptr, &token, &text)) != NULL) else if (token == LT_LIKE)
{ {
tree->right = make_tree_node(MAXOP_LIKE, if ((ptr = fetch_token(ptr, &token, &text)) != NULL)
text, NULL, NULL); {
return tree; tree->right = make_tree_node(MAXOP_LIKE,
} text, NULL, NULL);
else return tree;
{ }
// Expected expression else
*parse_error = PARSE_EXPECTED_LIKE; {
free_tree(tree); // Expected expression
return NULL; *parse_error = PARSE_EXPECTED_LIKE;
} maxinfo_free_tree(tree);
} return NULL;
// Malformed show }
free(text); }
free_tree(tree); // Malformed show
*parse_error = PARSE_MALFORMED_SHOW; free(text);
return NULL; maxinfo_free_tree(tree);
*parse_error = PARSE_MALFORMED_SHOW;
return NULL;
#if 0 #if 0
case LT_SELECT: case LT_SELECT:
free(text); // not needed free(text); // not needed
@ -128,7 +130,7 @@ MAXINFO_TREE *col, *table;
{ {
/** Unknown token after SHUTDOWN MONITOR|SERVICE */ /** Unknown token after SHUTDOWN MONITOR|SERVICE */
*parse_error = PARSE_SYNTAX_ERROR; *parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree); maxinfo_free_tree(tree);
return NULL; return NULL;
} }
return tree; return tree;
@ -142,7 +144,7 @@ MAXINFO_TREE *col, *table;
{ {
/** Missing token for RESTART MONITOR|SERVICE */ /** Missing token for RESTART MONITOR|SERVICE */
*parse_error = PARSE_SYNTAX_ERROR; *parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree); maxinfo_free_tree(tree);
return NULL; return NULL;
} }
tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL); tree->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL);
@ -152,7 +154,7 @@ MAXINFO_TREE *col, *table;
/** Unknown token after RESTART MONITOR|SERVICE */ /** Unknown token after RESTART MONITOR|SERVICE */
*parse_error = PARSE_SYNTAX_ERROR; *parse_error = PARSE_SYNTAX_ERROR;
free(text); free(text);
free_tree(tree); maxinfo_free_tree(tree);
return NULL; return NULL;
} }
return tree; return tree;
@ -270,20 +272,26 @@ MAXINFO_TREE *node;
} }
/** /**
* Recusrsively free the storage associated with a parse tree * Recursively free the storage associated with a parse tree
* *
* @param tree The parse tree to free * @param tree The parse tree to free
*/ */
static void void
free_tree(MAXINFO_TREE *tree) maxinfo_free_tree(MAXINFO_TREE *tree)
{ {
if (tree->left) if (tree->left)
free_tree(tree->left); {
if (tree->right) maxinfo_free_tree(tree->left);
free_tree(tree->right); }
if (tree->value) if (tree->right)
free(tree->value); {
free(tree); maxinfo_free_tree(tree->right);
}
if (tree->value)
{
free(tree->value);
}
free(tree);
} }
/** /**
@ -410,8 +418,8 @@ MAXINFO_TREE* maxinfo_parse_literals(MAXINFO_TREE *tree, int min_args, char *ptr
(node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL) (node->right = make_tree_node(MAXOP_LITERAL, text, NULL, NULL)) == NULL)
{ {
*parse_error = PARSE_SYNTAX_ERROR; *parse_error = PARSE_SYNTAX_ERROR;
free_tree(tree); maxinfo_free_tree(tree);
if(ptr) if (ptr)
{ {
free(text); free(text);
} }