jsonrpc: fix received JSON buffer overwrite

The receive buffer and JSON values array are shared across all requests
on one connection. If RPC handlers deferre processing response, do the
lazy decode or capture JSON by pointer instead of copying it then
content of the request array might be overwritten by now. As we don't
have any requirement here we must assert that the received request is
valid till response is finished.

Fix this issue by copying request data and work on the copy. This change
also void the need of having JSON RPC 'id' field releasing over 128
bytes from each spdk_jsonrpc_request.

Change-Id: I665be446cbcd8f625e5a73514582efad3021a4ff
Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com>
Reviewed-on: https://review.gerrithub.io/c/437160
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
This commit is contained in:
Pawel Wodkowski 2018-12-13 15:33:43 +01:00 committed by Jim Harris
parent 5b1b3ddf33
commit 6397801009
4 changed files with 188 additions and 204 deletions

View File

@ -51,8 +51,7 @@ struct spdk_jsonrpc_request {
struct spdk_jsonrpc_server_conn *conn; struct spdk_jsonrpc_server_conn *conn;
/* Copy of request id value */ /* Copy of request id value */
struct spdk_json_val id; const struct spdk_json_val *id;
uint8_t id_data[SPDK_JSONRPC_ID_MAX_LEN];
/* Total space allocated for send_buf */ /* Total space allocated for send_buf */
size_t send_buf_size; size_t send_buf_size;
@ -62,6 +61,10 @@ struct spdk_jsonrpc_request {
size_t send_offset; size_t send_offset;
uint8_t *recv_buffer;
struct spdk_json_val *values;
size_t values_cnt;
uint8_t *send_buf; uint8_t *send_buf;
STAILQ_ENTRY(spdk_jsonrpc_request) link; STAILQ_ENTRY(spdk_jsonrpc_request) link;
@ -71,7 +74,6 @@ struct spdk_jsonrpc_server_conn {
struct spdk_jsonrpc_server *server; struct spdk_jsonrpc_server *server;
int sockfd; int sockfd;
bool closed; bool closed;
struct spdk_json_val values[SPDK_JSONRPC_MAX_VALUES];
size_t recv_len; size_t recv_len;
uint8_t recv_buf[SPDK_JSONRPC_RECV_BUF_SIZE]; uint8_t recv_buf[SPDK_JSONRPC_RECV_BUF_SIZE];
uint32_t outstanding_requests; uint32_t outstanding_requests;
@ -140,7 +142,8 @@ void spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int
void spdk_jsonrpc_server_send_response(struct spdk_jsonrpc_request *request); void spdk_jsonrpc_server_send_response(struct spdk_jsonrpc_request *request);
/* jsonrpc_server */ /* jsonrpc_server */
int spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, size_t size); int spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, const void *json,
size_t size);
/* Must be called only from server poll thread */ /* Must be called only from server poll thread */
void spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request); void spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request);

View File

@ -61,71 +61,58 @@ static const struct spdk_json_object_decoder jsonrpc_request_decoders[] = {
static void static void
parse_single_request(struct spdk_jsonrpc_request *request, struct spdk_json_val *values) parse_single_request(struct spdk_jsonrpc_request *request, struct spdk_json_val *values)
{ {
bool invalid = false;
struct jsonrpc_request req = {}; struct jsonrpc_request req = {};
if (spdk_json_decode_object(values, jsonrpc_request_decoders, if (spdk_json_decode_object(values, jsonrpc_request_decoders,
SPDK_COUNTOF(jsonrpc_request_decoders), SPDK_COUNTOF(jsonrpc_request_decoders),
&req)) { &req)) {
invalid = true; goto invalid;
goto done;
} }
if (req.version && (req.version->type != SPDK_JSON_VAL_STRING || if (req.version && (req.version->type != SPDK_JSON_VAL_STRING ||
!spdk_json_strequal(req.version, "2.0"))) { !spdk_json_strequal(req.version, "2.0"))) {
invalid = true; goto invalid;
} }
if (!req.method || req.method->type != SPDK_JSON_VAL_STRING) { if (!req.method || req.method->type != SPDK_JSON_VAL_STRING) {
req.method = NULL; goto invalid;
invalid = true;
} }
if (req.id) { if (req.id) {
if (req.id->type == SPDK_JSON_VAL_STRING || if (req.id->type == SPDK_JSON_VAL_STRING ||
req.id->type == SPDK_JSON_VAL_NUMBER) { req.id->type == SPDK_JSON_VAL_NUMBER ||
/* Copy value into request */ req.id->type == SPDK_JSON_VAL_NULL) {
if (req.id->len <= SPDK_JSONRPC_ID_MAX_LEN) { request->id = req.id;
request->id.type = req.id->type;
request->id.len = req.id->len;
memcpy(request->id.start, req.id->start, req.id->len);
} else {
SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON-RPC request id too long (%u)\n",
req.id->len);
invalid = true;
}
} else if (req.id->type == SPDK_JSON_VAL_NULL) {
request->id.type = SPDK_JSON_VAL_NULL;
} else { } else {
invalid = true; goto invalid;
} }
} }
if (req.params) { if (req.params) {
if (req.params->type != SPDK_JSON_VAL_ARRAY_BEGIN && if (req.params->type != SPDK_JSON_VAL_ARRAY_BEGIN &&
req.params->type != SPDK_JSON_VAL_OBJECT_BEGIN) { req.params->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
req.params = NULL; goto invalid;
invalid = true;
} }
} }
done: spdk_jsonrpc_server_handle_request(request, req.method, req.params);
if (invalid) { return;
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
} else { invalid:
spdk_jsonrpc_server_handle_request(request, req.method, req.params); spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
}
} }
int int
spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, size_t size) spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, const void *json, size_t size)
{ {
struct spdk_jsonrpc_request *request; struct spdk_jsonrpc_request *request;
ssize_t rc; ssize_t rc;
size_t len;
void *end = NULL; void *end = NULL;
/* Check to see if we have received a full JSON value. */ /* Check to see if we have received a full JSON value. It is safe to cast away const
rc = spdk_json_parse(json, size, NULL, 0, &end, 0); * as we don't decode in place. */
rc = spdk_json_parse((void *)json, size, NULL, 0, &end, 0);
if (rc == SPDK_JSON_PARSE_INCOMPLETE) { if (rc == SPDK_JSON_PARSE_INCOMPLETE) {
return 0; return 0;
} }
@ -139,21 +126,40 @@ spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, si
conn->outstanding_requests++; conn->outstanding_requests++;
request->conn = conn; request->conn = conn;
request->id.start = request->id_data;
request->id.len = 0; len = end - json;
request->id.type = SPDK_JSON_VAL_INVALID; request->recv_buffer = malloc(len + 1);
if (request->recv_buffer == NULL) {
SPDK_ERRLOG("Failed to allocate buffer to copy request (%zu bytes)\n", len + 1);
spdk_jsonrpc_free_request(request);
return -1;
}
memcpy(request->recv_buffer, json, len);
request->recv_buffer[len] = '\0';
if (rc > 0 && rc <= SPDK_JSONRPC_MAX_VALUES) {
request->values_cnt = rc;
request->values = malloc(request->values_cnt * sizeof(request->values[0]));
if (request->values == NULL) {
SPDK_ERRLOG("Failed to allocate buffer for JSON values (%zu bytes)\n",
request->values_cnt * sizeof(request->values[0]));
spdk_jsonrpc_free_request(request);
return -1;
}
}
request->send_offset = 0; request->send_offset = 0;
request->send_len = 0; request->send_len = 0;
request->send_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT; request->send_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT;
request->send_buf = malloc(request->send_buf_size); request->send_buf = malloc(request->send_buf_size);
if (request->send_buf == NULL) { if (request->send_buf == NULL) {
SPDK_ERRLOG("Failed to allocate send_buf (%zu bytes)\n", request->send_buf_size); SPDK_ERRLOG("Failed to allocate send_buf (%zu bytes)\n", request->send_buf_size);
conn->outstanding_requests--; spdk_jsonrpc_free_request(request);
free(request);
return -1; return -1;
} }
if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) { if (rc <= 0 || rc > SPDK_JSONRPC_MAX_VALUES) {
SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON parse error\n"); SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON parse error\n");
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_PARSE_ERROR); spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_PARSE_ERROR);
@ -165,7 +171,7 @@ spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, si
} }
/* Decode a second time now that there is a full JSON value available. */ /* Decode a second time now that there is a full JSON value available. */
rc = spdk_json_parse(json, size, conn->values, SPDK_JSONRPC_MAX_VALUES, &end, rc = spdk_json_parse(request->recv_buffer, size, request->values, request->values_cnt, &end,
SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE);
if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) { if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) {
SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON parse error on second pass\n"); SPDK_DEBUGLOG(SPDK_LOG_RPC, "JSON parse error on second pass\n");
@ -175,9 +181,9 @@ spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, si
assert(end != NULL); assert(end != NULL);
if (conn->values[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) { if (request->values[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) {
parse_single_request(request, conn->values); parse_single_request(request, request->values);
} else if (conn->values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN) { } else if (request->values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN) {
SPDK_DEBUGLOG(SPDK_LOG_RPC, "Got batch array (not currently supported)\n"); SPDK_DEBUGLOG(SPDK_LOG_RPC, "Got batch array (not currently supported)\n");
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST); spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
} else { } else {
@ -185,7 +191,7 @@ spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, si
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST); spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
} }
return end - json; return len;
} }
struct spdk_jsonrpc_server_conn * struct spdk_jsonrpc_server_conn *
@ -245,7 +251,11 @@ begin_response(struct spdk_jsonrpc_request *request)
spdk_json_write_string(w, "2.0"); spdk_json_write_string(w, "2.0");
spdk_json_write_name(w, "id"); spdk_json_write_name(w, "id");
spdk_json_write_val(w, &request->id); if (request->id) {
spdk_json_write_val(w, request->id);
} else {
spdk_json_write_null(w);
}
return w; return w;
} }
@ -269,7 +279,13 @@ end_response(struct spdk_jsonrpc_request *request, struct spdk_json_write_ctx *w
void void
spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request) spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request)
{ {
if (!request) {
return;
}
request->conn->outstanding_requests--; request->conn->outstanding_requests--;
free(request->recv_buffer);
free(request->values);
free(request->send_buf); free(request->send_buf);
free(request); free(request);
} }
@ -279,7 +295,7 @@ spdk_jsonrpc_begin_result(struct spdk_jsonrpc_request *request)
{ {
struct spdk_json_write_ctx *w; struct spdk_json_write_ctx *w;
if (request->id.type == SPDK_JSON_VAL_INVALID) { if (request->id == NULL || request->id->type == SPDK_JSON_VAL_NULL) {
/* Notification - no response required */ /* Notification - no response required */
skip_response(request); skip_response(request);
return NULL; return NULL;
@ -310,11 +326,6 @@ spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request *request,
{ {
struct spdk_json_write_ctx *w; struct spdk_json_write_ctx *w;
if (request->id.type == SPDK_JSON_VAL_INVALID) {
/* For error responses, if id is missing, explicitly respond with "id": null. */
request->id.type = SPDK_JSON_VAL_NULL;
}
w = begin_response(request); w = begin_response(request);
if (w == NULL) { if (w == NULL) {
skip_response(request); skip_response(request);
@ -339,11 +350,6 @@ spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request *request,
struct spdk_json_write_ctx *w; struct spdk_json_write_ctx *w;
va_list args; va_list args;
if (request->id.type == SPDK_JSON_VAL_INVALID) {
/* For error responses, if id is missing, explicitly respond with "id": null. */
request->id.type = SPDK_JSON_VAL_NULL;
}
w = begin_response(request); w = begin_response(request);
if (w == NULL) { if (w == NULL) {
skip_response(request); skip_response(request);

View File

@ -391,10 +391,8 @@ spdk_jsonrpc_server_poll(struct spdk_jsonrpc_server *server)
* the connection is closed). * the connection is closed).
*/ */
if (conn->send_request) { spdk_jsonrpc_free_request(conn->send_request);
spdk_jsonrpc_free_request(conn->send_request); conn->send_request = NULL;
conn->send_request = NULL;
}
while ((request = spdk_jsonrpc_server_dequeue_request(conn)) != NULL) { while ((request = spdk_jsonrpc_server_dequeue_request(conn)) != NULL) {
spdk_jsonrpc_free_request(request); spdk_jsonrpc_free_request(request);

View File

@ -37,177 +37,124 @@
#include "jsonrpc/jsonrpc_server.c" #include "jsonrpc/jsonrpc_server.c"
#define MAX_PARAMS 100 static struct spdk_jsonrpc_request *g_request;
#define MAX_REQS 100 static int g_parse_error;
const struct spdk_json_val *g_method;
const struct spdk_json_val *g_params;
struct req { const struct spdk_json_val *g_cur_param;
int error;
bool got_method;
bool got_id;
bool got_params;
struct spdk_jsonrpc_request *request;
struct spdk_json_val method;
struct spdk_json_val id;
struct spdk_json_val params[MAX_PARAMS];
};
static uint8_t g_buf[1000];
static struct req g_reqs[MAX_REQS];
static struct req *g_cur_req;
static struct spdk_json_val *g_params;
static size_t g_num_reqs;
#define PARSE_PASS(in, trailing) \ #define PARSE_PASS(in, trailing) \
memcpy(g_buf, in, sizeof(in) - 1); \ CU_ASSERT(g_cur_param == NULL); \
g_num_reqs = 0; \ g_cur_param = NULL; \
g_cur_req = NULL; \ CU_ASSERT(spdk_jsonrpc_parse_request(conn, in, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, sizeof(in) - 1) == sizeof(in) - sizeof(trailing)); \
if (g_cur_req && g_cur_req->request) { \ #define REQ_BEGIN(expected_error) \
free(g_cur_req->request->send_buf); \ if (expected_error != 0 ) { \
g_cur_req->request->send_buf = NULL; \ CU_ASSERT(g_parse_error == expected_error); \
CU_ASSERT(g_params == NULL); \
} }
#define PARSE_FAIL(in) \ #define PARSE_FAIL(in) \
memcpy(g_buf, in, sizeof(in) - 1); \ CU_ASSERT(spdk_jsonrpc_parse_request(conn, in, sizeof(in) - 1) < 0);
g_num_reqs = 0; \
g_cur_req = 0; \
CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, sizeof(in) - 1) < 0); \
if (g_cur_req && g_cur_req->request) { \
free(g_cur_req->request->send_buf); \
g_cur_req->request->send_buf = NULL; \
}
#define REQ_BEGIN(expected_error) \ #define REQ_BEGIN_VALID() \
if (g_cur_req == NULL) { \ REQ_BEGIN(0); \
g_cur_req = g_reqs; \ SPDK_CU_ASSERT_FATAL(g_params != NULL);
} else { \
g_cur_req++; \ #define REQ_BEGIN_INVALID(expected_error) \
} \ REQ_BEGIN(expected_error); \
CU_ASSERT(g_cur_req - g_reqs <= (ptrdiff_t)g_num_reqs); \ REQ_METHOD_MISSING(); \
CU_ASSERT(g_cur_req->error == expected_error) REQ_ID_MISSING(); \
REQ_PARAMS_MISSING()
#define REQ_BEGIN_VALID() REQ_BEGIN(0)
#define REQ_BEGIN_INVALID(expected_error) REQ_BEGIN(expected_error)
#define REQ_METHOD(name) \ #define REQ_METHOD(name) \
CU_ASSERT(g_cur_req->got_method); \ CU_ASSERT(g_method && spdk_json_strequal(g_method, name) == true)
CU_ASSERT(spdk_json_strequal(&g_cur_req->method, name) == true)
#define REQ_METHOD_MISSING() \ #define REQ_METHOD_MISSING() \
CU_ASSERT(g_cur_req->got_method == false) CU_ASSERT(g_method == NULL)
#define REQ_ID_NUM(num) \ #define REQ_ID_NUM(num) \
CU_ASSERT(g_cur_req->got_id); \ CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NUMBER); \
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NUMBER); \ CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, sizeof(num) - 1) == 0)
CU_ASSERT(memcmp(g_cur_req->id.start, num, sizeof(num) - 1) == 0)
#define REQ_ID_STRING(str) \ #define REQ_ID_STRING(str) \
CU_ASSERT(g_cur_req->got_id); \ CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_STRING); \
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_STRING); \ CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, strlen(num) - 1) == 0))
CU_ASSERT(memcmp(g_cur_req->id.start, str, sizeof(str) - 1) == 0)
#define REQ_ID_NULL() \ #define REQ_ID_NULL() \
CU_ASSERT(g_cur_req->got_id); \ CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NULL)
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NULL)
#define REQ_ID_MISSING() \ #define REQ_ID_MISSING() \
CU_ASSERT(g_cur_req->got_id == false) CU_ASSERT(g_request->id == NULL)
#define REQ_PARAMS_MISSING() \ #define REQ_PARAMS_MISSING() \
CU_ASSERT(g_cur_req->got_params == false) CU_ASSERT(g_params == NULL)
#define REQ_PARAMS_BEGIN() \ #define REQ_PARAMS_BEGIN() \
CU_ASSERT(g_cur_req->got_params); \ SPDK_CU_ASSERT_FATAL(g_params != NULL); \
g_params = g_cur_req->params CU_ASSERT(g_cur_param == NULL); \
g_cur_param = g_params
#define PARAM_ARRAY_BEGIN() \ #define PARAM_ARRAY_BEGIN() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_BEGIN); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
g_params++ g_cur_param++
#define PARAM_ARRAY_END() \ #define PARAM_ARRAY_END() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_END); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_END); \
g_params++ g_cur_param++
#define PARAM_OBJECT_BEGIN() \ #define PARAM_OBJECT_BEGIN() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_BEGIN); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
g_params++ g_cur_param++
#define PARAM_OBJECT_END() \ #define PARAM_OBJECT_END() \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_END); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_END); \
g_params++ g_cur_param++
#define PARAM_NUM(num) \ #define PARAM_NUM(num) \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_NUMBER); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NUMBER); \
CU_ASSERT(g_params->len == sizeof(num) - 1); \ CU_ASSERT(g_cur_param->len == sizeof(num) - 1); \
CU_ASSERT(memcmp(g_params->start, num, g_params->len) == 0); \ CU_ASSERT(memcmp(g_cur_param->start, num, sizeof(num) - 1) == 0); \
g_params++ g_cur_param++
#define PARAM_NAME(str) \ #define PARAM_NAME(str) \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_NAME); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NAME); \
CU_ASSERT(g_params->len == sizeof(str) - 1); \ CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \ CU_ASSERT(g_cur_param && memcmp(g_cur_param->start, str, sizeof(str) - 1) == 0); \
g_params++ g_cur_param++
#define PARAM_STRING(str) \ #define PARAM_STRING(str) \
CU_ASSERT(g_params->type == SPDK_JSON_VAL_STRING); \ CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_STRING); \
CU_ASSERT(g_params->len == sizeof(str) - 1); \ CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \ CU_ASSERT(memcmp(g_cur_param->start, str, g_params->len) == 0); \
g_params++ g_cur_param++
#define FREE_REQUEST() \ #define FREE_REQUEST() \
if (g_reqs->request) { \ spdk_jsonrpc_free_request(g_request); \
free(g_reqs->request->send_buf); \ g_request = NULL; \
} \ g_cur_param = NULL; \
free(g_reqs->request); \ g_parse_error = 0; \
g_reqs->request = NULL g_method = NULL; \
g_cur_param = g_params = NULL
static void static void
ut_handle(struct spdk_jsonrpc_request *request, int error, const struct spdk_json_val *method, ut_handle(struct spdk_jsonrpc_request *request, int error, const struct spdk_json_val *method,
const struct spdk_json_val *params) const struct spdk_json_val *params)
{ {
const struct spdk_json_val *id = &request->id; CU_ASSERT(g_request == NULL);
struct req *r; g_request = request;
g_parse_error = error;
SPDK_CU_ASSERT_FATAL(g_num_reqs != MAX_REQS); g_method = method;
r = &g_reqs[g_num_reqs++]; g_params = params;
r->request = request;
r->error = error;
if (method) {
r->got_method = true;
r->method = *method;
} else {
r->got_method = false;
}
if (params) {
r->got_params = true;
SPDK_CU_ASSERT_FATAL(spdk_json_val_len(params) < MAX_PARAMS);
memcpy(r->params, params, spdk_json_val_len(params) * sizeof(struct spdk_json_val));
} else {
r->got_params = false;
}
if (id && id->type != SPDK_JSON_VAL_INVALID) {
r->got_id = true;
r->id = *id;
} else {
r->got_id = false;
}
} }
void void
spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error) spdk_jsonrpc_server_handle_error(struct spdk_jsonrpc_request *request, int error)
{ {
/*
* Map missing id to Null - this mirrors the behavior in the real
* spdk_jsonrpc_server_handle_error() function.
*/
if (request->id.type == SPDK_JSON_VAL_INVALID) {
request->id.type = SPDK_JSON_VAL_NULL;
}
ut_handle(request, error, NULL, NULL); ut_handle(request, error, NULL, NULL);
} }
@ -238,6 +185,26 @@ test_parse_request(void)
conn->server = server; conn->server = server;
/* rpc call with no parameters. */
PARSE_PASS("{ }", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
FREE_REQUEST();
/* rpc call with method that is not a string. */
PARSE_PASS("{\"jsonrpc\":\"2.0\", \"method\": null }", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
FREE_REQUEST();
/* rpc call with invalid JSON RPC version. */
PARSE_PASS("{\"jsonrpc\":\"42\", \"method\": \"subtract\"}", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
FREE_REQUEST();
/* rpc call with embedded zeros. */
PARSE_FAIL("{\"jsonrpc\":\"2.0\",\"method\":\"foo\",\"params\":{\"bar\": \"\0\0baz\"}}");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
FREE_REQUEST();
/* rpc call with positional parameters */ /* rpc call with positional parameters */
PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", ""); PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
REQ_BEGIN_VALID(); REQ_BEGIN_VALID();
@ -280,20 +247,30 @@ test_parse_request(void)
PARAM_ARRAY_END(); PARAM_ARRAY_END();
FREE_REQUEST(); FREE_REQUEST();
/* notification with explicit NULL ID. This is discouraged by JSON RPC spec but allowed. */
PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": \"update\", \"params\": [1,2,3,4,5], \"id\": null}",
"");
REQ_BEGIN_VALID();
REQ_METHOD("update");
REQ_ID_NULL();
REQ_PARAMS_BEGIN();
PARAM_ARRAY_BEGIN();
PARAM_NUM("1");
PARAM_NUM("2");
PARAM_NUM("3");
PARAM_NUM("4");
PARAM_NUM("5");
PARAM_ARRAY_END();
FREE_REQUEST();
/* invalid JSON */ /* invalid JSON */
PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]"); PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR); REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
REQ_METHOD_MISSING();
REQ_ID_NULL();
REQ_PARAMS_MISSING();
FREE_REQUEST(); FREE_REQUEST();
/* invalid request (method must be a string; params must be array or object) */ /* invalid request (method must be a string; params must be array or object) */
PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", ""); PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST); REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_NULL();
REQ_PARAMS_MISSING();
FREE_REQUEST(); FREE_REQUEST();
/* batch, invalid JSON */ /* batch, invalid JSON */
@ -303,17 +280,11 @@ test_parse_request(void)
"{\"jsonrpc\": \"2.0\", \"method\"" "{\"jsonrpc\": \"2.0\", \"method\""
"]"); "]");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR); REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
REQ_METHOD_MISSING();
REQ_ID_NULL();
REQ_PARAMS_MISSING();
FREE_REQUEST(); FREE_REQUEST();
/* empty array */ /* empty array */
PARSE_PASS("[]", ""); PARSE_PASS("[]", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST); REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_NULL();
REQ_PARAMS_MISSING();
FREE_REQUEST(); FREE_REQUEST();
/* batch - not supported */ /* batch - not supported */
@ -328,11 +299,9 @@ test_parse_request(void)
"]", ""); "]", "");
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST); REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
REQ_METHOD_MISSING();
REQ_ID_NULL();
REQ_PARAMS_MISSING();
FREE_REQUEST(); FREE_REQUEST();
CU_ASSERT(conn->outstanding_requests == 0);
free(conn); free(conn);
free(server); free(server);
} }
@ -342,6 +311,7 @@ test_parse_request_streaming(void)
{ {
struct spdk_jsonrpc_server *server; struct spdk_jsonrpc_server *server;
struct spdk_jsonrpc_server_conn *conn; struct spdk_jsonrpc_server_conn *conn;
const char *json_req;
size_t len, i; size_t len, i;
server = calloc(1, sizeof(*server)); server = calloc(1, sizeof(*server));
@ -352,6 +322,7 @@ test_parse_request_streaming(void)
conn->server = server; conn->server = server;
/* /*
* Two valid requests end to end in the same buffer. * Two valid requests end to end in the same buffer.
* Parse should return the first one and point to the beginning of the second one. * Parse should return the first one and point to the beginning of the second one.
@ -360,6 +331,7 @@ test_parse_request_streaming(void)
"{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}" "{\"jsonrpc\":\"2.0\",\"method\":\"a\",\"params\":[1],\"id\":1}"
"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}", "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}",
"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}"); "{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
REQ_BEGIN_VALID(); REQ_BEGIN_VALID();
REQ_METHOD("a"); REQ_METHOD("a");
REQ_ID_NUM("1"); REQ_ID_NUM("1");
@ -370,22 +342,25 @@ test_parse_request_streaming(void)
FREE_REQUEST(); FREE_REQUEST();
/* Partial (but not invalid) requests - parse should not consume anything. */ /* Partial (but not invalid) requests - parse should not consume anything. */
snprintf(g_buf, sizeof(g_buf), "%s", json_req = " {\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}";
"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}"); len = strlen(json_req);
len = strlen(g_buf);
/* Try every partial length up to the full request length */ /* Try every partial length up to the full request length */
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
int rc = spdk_jsonrpc_parse_request(conn, g_buf, i); int rc = spdk_jsonrpc_parse_request(conn, json_req, i);
/* Partial request - no data consumed */ /* Partial request - no data consumed */
CU_ASSERT(rc == 0); CU_ASSERT(rc == 0);
CU_ASSERT(g_request == NULL);
/* In case of faile, don't fload console with ussless CU assert fails. */
FREE_REQUEST(); FREE_REQUEST();
} }
/* Verify that full request can be parsed successfully */ /* Verify that full request can be parsed successfully */
CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, len) == (ssize_t)len); CU_ASSERT(spdk_jsonrpc_parse_request(conn, json_req, len) == (ssize_t)len);
FREE_REQUEST(); FREE_REQUEST();
CU_ASSERT(conn->outstanding_requests == 0);
free(conn); free(conn);
free(server); free(server);
} }
@ -411,7 +386,6 @@ int main(int argc, char **argv)
CU_cleanup_registry(); CU_cleanup_registry();
return CU_get_error(); return CU_get_error();
} }
CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_set_mode(CU_BRM_VERBOSE);
CU_basic_run_tests(); CU_basic_run_tests();
@ -419,5 +393,8 @@ int main(int argc, char **argv)
num_failures = CU_get_number_of_failures(); num_failures = CU_get_number_of_failures();
CU_cleanup_registry(); CU_cleanup_registry();
/* This is for ASAN. Don't know why but if pointer is left in global varaible
* it won't be detected as leak. */
g_request = NULL;
return num_failures; return num_failures;
} }