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:
parent
5b1b3ddf33
commit
6397801009
@ -51,8 +51,7 @@ struct spdk_jsonrpc_request {
|
||||
struct spdk_jsonrpc_server_conn *conn;
|
||||
|
||||
/* Copy of request id value */
|
||||
struct spdk_json_val id;
|
||||
uint8_t id_data[SPDK_JSONRPC_ID_MAX_LEN];
|
||||
const struct spdk_json_val *id;
|
||||
|
||||
/* Total space allocated for send_buf */
|
||||
size_t send_buf_size;
|
||||
@ -62,6 +61,10 @@ struct spdk_jsonrpc_request {
|
||||
|
||||
size_t send_offset;
|
||||
|
||||
uint8_t *recv_buffer;
|
||||
struct spdk_json_val *values;
|
||||
size_t values_cnt;
|
||||
|
||||
uint8_t *send_buf;
|
||||
|
||||
STAILQ_ENTRY(spdk_jsonrpc_request) link;
|
||||
@ -71,7 +74,6 @@ struct spdk_jsonrpc_server_conn {
|
||||
struct spdk_jsonrpc_server *server;
|
||||
int sockfd;
|
||||
bool closed;
|
||||
struct spdk_json_val values[SPDK_JSONRPC_MAX_VALUES];
|
||||
size_t recv_len;
|
||||
uint8_t recv_buf[SPDK_JSONRPC_RECV_BUF_SIZE];
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
void spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request);
|
||||
|
@ -61,71 +61,58 @@ static const struct spdk_json_object_decoder jsonrpc_request_decoders[] = {
|
||||
static void
|
||||
parse_single_request(struct spdk_jsonrpc_request *request, struct spdk_json_val *values)
|
||||
{
|
||||
bool invalid = false;
|
||||
struct jsonrpc_request req = {};
|
||||
|
||||
if (spdk_json_decode_object(values, jsonrpc_request_decoders,
|
||||
SPDK_COUNTOF(jsonrpc_request_decoders),
|
||||
&req)) {
|
||||
invalid = true;
|
||||
goto done;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (req.version && (req.version->type != SPDK_JSON_VAL_STRING ||
|
||||
!spdk_json_strequal(req.version, "2.0"))) {
|
||||
invalid = true;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (!req.method || req.method->type != SPDK_JSON_VAL_STRING) {
|
||||
req.method = NULL;
|
||||
invalid = true;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (req.id) {
|
||||
if (req.id->type == SPDK_JSON_VAL_STRING ||
|
||||
req.id->type == SPDK_JSON_VAL_NUMBER) {
|
||||
/* Copy value into request */
|
||||
if (req.id->len <= SPDK_JSONRPC_ID_MAX_LEN) {
|
||||
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;
|
||||
req.id->type == SPDK_JSON_VAL_NUMBER ||
|
||||
req.id->type == SPDK_JSON_VAL_NULL) {
|
||||
request->id = req.id;
|
||||
} else {
|
||||
invalid = true;
|
||||
goto invalid;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.params) {
|
||||
if (req.params->type != SPDK_JSON_VAL_ARRAY_BEGIN &&
|
||||
req.params->type != SPDK_JSON_VAL_OBJECT_BEGIN) {
|
||||
req.params = NULL;
|
||||
invalid = true;
|
||||
goto invalid;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (invalid) {
|
||||
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
|
||||
} else {
|
||||
spdk_jsonrpc_server_handle_request(request, req.method, req.params);
|
||||
}
|
||||
spdk_jsonrpc_server_handle_request(request, req.method, req.params);
|
||||
return;
|
||||
|
||||
invalid:
|
||||
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
|
||||
}
|
||||
|
||||
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;
|
||||
ssize_t rc;
|
||||
size_t len;
|
||||
void *end = NULL;
|
||||
|
||||
/* Check to see if we have received a full JSON value. */
|
||||
rc = spdk_json_parse(json, size, NULL, 0, &end, 0);
|
||||
/* Check to see if we have received a full JSON value. It is safe to cast away const
|
||||
* as we don't decode in place. */
|
||||
rc = spdk_json_parse((void *)json, size, NULL, 0, &end, 0);
|
||||
if (rc == SPDK_JSON_PARSE_INCOMPLETE) {
|
||||
return 0;
|
||||
}
|
||||
@ -139,21 +126,40 @@ spdk_jsonrpc_parse_request(struct spdk_jsonrpc_server_conn *conn, void *json, si
|
||||
conn->outstanding_requests++;
|
||||
|
||||
request->conn = conn;
|
||||
request->id.start = request->id_data;
|
||||
request->id.len = 0;
|
||||
request->id.type = SPDK_JSON_VAL_INVALID;
|
||||
|
||||
len = end - json;
|
||||
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_len = 0;
|
||||
request->send_buf_size = SPDK_JSONRPC_SEND_BUF_SIZE_INIT;
|
||||
request->send_buf = malloc(request->send_buf_size);
|
||||
if (request->send_buf == NULL) {
|
||||
SPDK_ERRLOG("Failed to allocate send_buf (%zu bytes)\n", request->send_buf_size);
|
||||
conn->outstanding_requests--;
|
||||
free(request);
|
||||
spdk_jsonrpc_free_request(request);
|
||||
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_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. */
|
||||
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);
|
||||
if (rc < 0 || rc > SPDK_JSONRPC_MAX_VALUES) {
|
||||
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);
|
||||
|
||||
if (conn->values[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) {
|
||||
parse_single_request(request, conn->values);
|
||||
} else if (conn->values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN) {
|
||||
if (request->values[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) {
|
||||
parse_single_request(request, request->values);
|
||||
} else if (request->values[0].type == SPDK_JSON_VAL_ARRAY_BEGIN) {
|
||||
SPDK_DEBUGLOG(SPDK_LOG_RPC, "Got batch array (not currently supported)\n");
|
||||
spdk_jsonrpc_server_handle_error(request, SPDK_JSONRPC_ERROR_INVALID_REQUEST);
|
||||
} 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);
|
||||
}
|
||||
|
||||
return end - json;
|
||||
return len;
|
||||
}
|
||||
|
||||
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_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;
|
||||
}
|
||||
@ -269,7 +279,13 @@ end_response(struct spdk_jsonrpc_request *request, struct spdk_json_write_ctx *w
|
||||
void
|
||||
spdk_jsonrpc_free_request(struct spdk_jsonrpc_request *request)
|
||||
{
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
|
||||
request->conn->outstanding_requests--;
|
||||
free(request->recv_buffer);
|
||||
free(request->values);
|
||||
free(request->send_buf);
|
||||
free(request);
|
||||
}
|
||||
@ -279,7 +295,7 @@ spdk_jsonrpc_begin_result(struct spdk_jsonrpc_request *request)
|
||||
{
|
||||
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 */
|
||||
skip_response(request);
|
||||
return NULL;
|
||||
@ -310,11 +326,6 @@ spdk_jsonrpc_send_error_response(struct spdk_jsonrpc_request *request,
|
||||
{
|
||||
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);
|
||||
if (w == NULL) {
|
||||
skip_response(request);
|
||||
@ -339,11 +350,6 @@ spdk_jsonrpc_send_error_response_fmt(struct spdk_jsonrpc_request *request,
|
||||
struct spdk_json_write_ctx *w;
|
||||
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);
|
||||
if (w == NULL) {
|
||||
skip_response(request);
|
||||
|
@ -391,10 +391,8 @@ spdk_jsonrpc_server_poll(struct spdk_jsonrpc_server *server)
|
||||
* the connection is closed).
|
||||
*/
|
||||
|
||||
if (conn->send_request) {
|
||||
spdk_jsonrpc_free_request(conn->send_request);
|
||||
conn->send_request = NULL;
|
||||
}
|
||||
spdk_jsonrpc_free_request(conn->send_request);
|
||||
conn->send_request = NULL;
|
||||
|
||||
while ((request = spdk_jsonrpc_server_dequeue_request(conn)) != NULL) {
|
||||
spdk_jsonrpc_free_request(request);
|
||||
|
@ -37,177 +37,124 @@
|
||||
|
||||
#include "jsonrpc/jsonrpc_server.c"
|
||||
|
||||
#define MAX_PARAMS 100
|
||||
#define MAX_REQS 100
|
||||
static struct spdk_jsonrpc_request *g_request;
|
||||
static int g_parse_error;
|
||||
const struct spdk_json_val *g_method;
|
||||
const struct spdk_json_val *g_params;
|
||||
|
||||
struct req {
|
||||
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;
|
||||
const struct spdk_json_val *g_cur_param;
|
||||
|
||||
#define PARSE_PASS(in, trailing) \
|
||||
memcpy(g_buf, in, sizeof(in) - 1); \
|
||||
g_num_reqs = 0; \
|
||||
g_cur_req = NULL; \
|
||||
CU_ASSERT(spdk_jsonrpc_parse_request(conn, g_buf, sizeof(in) - 1) == sizeof(in) - sizeof(trailing)); \
|
||||
if (g_cur_req && g_cur_req->request) { \
|
||||
free(g_cur_req->request->send_buf); \
|
||||
g_cur_req->request->send_buf = NULL; \
|
||||
CU_ASSERT(g_cur_param == NULL); \
|
||||
g_cur_param = NULL; \
|
||||
CU_ASSERT(spdk_jsonrpc_parse_request(conn, in, sizeof(in) - 1) == sizeof(in) - sizeof(trailing))
|
||||
|
||||
#define REQ_BEGIN(expected_error) \
|
||||
if (expected_error != 0 ) { \
|
||||
CU_ASSERT(g_parse_error == expected_error); \
|
||||
CU_ASSERT(g_params == NULL); \
|
||||
}
|
||||
|
||||
#define PARSE_FAIL(in) \
|
||||
memcpy(g_buf, in, sizeof(in) - 1); \
|
||||
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; \
|
||||
}
|
||||
CU_ASSERT(spdk_jsonrpc_parse_request(conn, in, sizeof(in) - 1) < 0);
|
||||
|
||||
#define REQ_BEGIN(expected_error) \
|
||||
if (g_cur_req == NULL) { \
|
||||
g_cur_req = g_reqs; \
|
||||
} else { \
|
||||
g_cur_req++; \
|
||||
} \
|
||||
CU_ASSERT(g_cur_req - g_reqs <= (ptrdiff_t)g_num_reqs); \
|
||||
CU_ASSERT(g_cur_req->error == expected_error)
|
||||
#define REQ_BEGIN_VALID() \
|
||||
REQ_BEGIN(0); \
|
||||
SPDK_CU_ASSERT_FATAL(g_params != NULL);
|
||||
|
||||
#define REQ_BEGIN_INVALID(expected_error) \
|
||||
REQ_BEGIN(expected_error); \
|
||||
REQ_METHOD_MISSING(); \
|
||||
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) \
|
||||
CU_ASSERT(g_cur_req->got_method); \
|
||||
CU_ASSERT(spdk_json_strequal(&g_cur_req->method, name) == true)
|
||||
CU_ASSERT(g_method && spdk_json_strequal(g_method, name) == true)
|
||||
|
||||
#define REQ_METHOD_MISSING() \
|
||||
CU_ASSERT(g_cur_req->got_method == false)
|
||||
CU_ASSERT(g_method == NULL)
|
||||
|
||||
#define REQ_ID_NUM(num) \
|
||||
CU_ASSERT(g_cur_req->got_id); \
|
||||
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NUMBER); \
|
||||
CU_ASSERT(memcmp(g_cur_req->id.start, num, sizeof(num) - 1) == 0)
|
||||
CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NUMBER); \
|
||||
CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, sizeof(num) - 1) == 0)
|
||||
|
||||
|
||||
#define REQ_ID_STRING(str) \
|
||||
CU_ASSERT(g_cur_req->got_id); \
|
||||
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_STRING); \
|
||||
CU_ASSERT(memcmp(g_cur_req->id.start, str, sizeof(str) - 1) == 0)
|
||||
CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_STRING); \
|
||||
CU_ASSERT(g_request->id && memcmp(g_request->id->start, num, strlen(num) - 1) == 0))
|
||||
|
||||
#define REQ_ID_NULL() \
|
||||
CU_ASSERT(g_cur_req->got_id); \
|
||||
CU_ASSERT(g_cur_req->id.type == SPDK_JSON_VAL_NULL)
|
||||
CU_ASSERT(g_request->id && g_request->id->type == SPDK_JSON_VAL_NULL)
|
||||
|
||||
#define REQ_ID_MISSING() \
|
||||
CU_ASSERT(g_cur_req->got_id == false)
|
||||
CU_ASSERT(g_request->id == NULL)
|
||||
|
||||
#define REQ_PARAMS_MISSING() \
|
||||
CU_ASSERT(g_cur_req->got_params == false)
|
||||
CU_ASSERT(g_params == NULL)
|
||||
|
||||
#define REQ_PARAMS_BEGIN() \
|
||||
CU_ASSERT(g_cur_req->got_params); \
|
||||
g_params = g_cur_req->params
|
||||
SPDK_CU_ASSERT_FATAL(g_params != NULL); \
|
||||
CU_ASSERT(g_cur_param == NULL); \
|
||||
g_cur_param = g_params
|
||||
|
||||
#define PARAM_ARRAY_BEGIN() \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_BEGIN); \
|
||||
g_cur_param++
|
||||
|
||||
#define PARAM_ARRAY_END() \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_ARRAY_END); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_ARRAY_END); \
|
||||
g_cur_param++
|
||||
|
||||
#define PARAM_OBJECT_BEGIN() \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_BEGIN); \
|
||||
g_cur_param++
|
||||
|
||||
#define PARAM_OBJECT_END() \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_OBJECT_END); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_OBJECT_END); \
|
||||
g_cur_param++
|
||||
|
||||
#define PARAM_NUM(num) \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_NUMBER); \
|
||||
CU_ASSERT(g_params->len == sizeof(num) - 1); \
|
||||
CU_ASSERT(memcmp(g_params->start, num, g_params->len) == 0); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NUMBER); \
|
||||
CU_ASSERT(g_cur_param->len == sizeof(num) - 1); \
|
||||
CU_ASSERT(memcmp(g_cur_param->start, num, sizeof(num) - 1) == 0); \
|
||||
g_cur_param++
|
||||
|
||||
#define PARAM_NAME(str) \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_NAME); \
|
||||
CU_ASSERT(g_params->len == sizeof(str) - 1); \
|
||||
CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_NAME); \
|
||||
CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
|
||||
CU_ASSERT(g_cur_param && memcmp(g_cur_param->start, str, sizeof(str) - 1) == 0); \
|
||||
g_cur_param++
|
||||
|
||||
#define PARAM_STRING(str) \
|
||||
CU_ASSERT(g_params->type == SPDK_JSON_VAL_STRING); \
|
||||
CU_ASSERT(g_params->len == sizeof(str) - 1); \
|
||||
CU_ASSERT(memcmp(g_params->start, str, g_params->len) == 0); \
|
||||
g_params++
|
||||
CU_ASSERT(g_cur_param->type == SPDK_JSON_VAL_STRING); \
|
||||
CU_ASSERT(g_cur_param->len == sizeof(str) - 1); \
|
||||
CU_ASSERT(memcmp(g_cur_param->start, str, g_params->len) == 0); \
|
||||
g_cur_param++
|
||||
|
||||
#define FREE_REQUEST() \
|
||||
if (g_reqs->request) { \
|
||||
free(g_reqs->request->send_buf); \
|
||||
} \
|
||||
free(g_reqs->request); \
|
||||
g_reqs->request = NULL
|
||||
spdk_jsonrpc_free_request(g_request); \
|
||||
g_request = NULL; \
|
||||
g_cur_param = NULL; \
|
||||
g_parse_error = 0; \
|
||||
g_method = NULL; \
|
||||
g_cur_param = g_params = NULL
|
||||
|
||||
|
||||
static void
|
||||
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 *id = &request->id;
|
||||
struct req *r;
|
||||
|
||||
SPDK_CU_ASSERT_FATAL(g_num_reqs != MAX_REQS);
|
||||
r = &g_reqs[g_num_reqs++];
|
||||
|
||||
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;
|
||||
}
|
||||
CU_ASSERT(g_request == NULL);
|
||||
g_request = request;
|
||||
g_parse_error = error;
|
||||
g_method = method;
|
||||
g_params = params;
|
||||
}
|
||||
|
||||
void
|
||||
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);
|
||||
}
|
||||
|
||||
@ -238,6 +185,26 @@ test_parse_request(void)
|
||||
|
||||
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 */
|
||||
PARSE_PASS("{\"jsonrpc\":\"2.0\",\"method\":\"subtract\",\"params\":[42,23],\"id\":1}", "");
|
||||
REQ_BEGIN_VALID();
|
||||
@ -280,20 +247,30 @@ test_parse_request(void)
|
||||
PARAM_ARRAY_END();
|
||||
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 */
|
||||
PARSE_FAIL("{\"jsonrpc\": \"2.0\", \"method\": \"foobar, \"params\": \"bar\", \"baz]");
|
||||
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
|
||||
REQ_METHOD_MISSING();
|
||||
REQ_ID_NULL();
|
||||
REQ_PARAMS_MISSING();
|
||||
FREE_REQUEST();
|
||||
|
||||
/* invalid request (method must be a string; params must be array or object) */
|
||||
PARSE_PASS("{\"jsonrpc\": \"2.0\", \"method\": 1, \"params\": \"bar\"}", "");
|
||||
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
|
||||
REQ_METHOD_MISSING();
|
||||
REQ_ID_NULL();
|
||||
REQ_PARAMS_MISSING();
|
||||
FREE_REQUEST();
|
||||
|
||||
/* batch, invalid JSON */
|
||||
@ -303,17 +280,11 @@ test_parse_request(void)
|
||||
"{\"jsonrpc\": \"2.0\", \"method\""
|
||||
"]");
|
||||
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_PARSE_ERROR);
|
||||
REQ_METHOD_MISSING();
|
||||
REQ_ID_NULL();
|
||||
REQ_PARAMS_MISSING();
|
||||
FREE_REQUEST();
|
||||
|
||||
/* empty array */
|
||||
PARSE_PASS("[]", "");
|
||||
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
|
||||
REQ_METHOD_MISSING();
|
||||
REQ_ID_NULL();
|
||||
REQ_PARAMS_MISSING();
|
||||
FREE_REQUEST();
|
||||
|
||||
/* batch - not supported */
|
||||
@ -328,11 +299,9 @@ test_parse_request(void)
|
||||
"]", "");
|
||||
|
||||
REQ_BEGIN_INVALID(SPDK_JSONRPC_ERROR_INVALID_REQUEST);
|
||||
REQ_METHOD_MISSING();
|
||||
REQ_ID_NULL();
|
||||
REQ_PARAMS_MISSING();
|
||||
FREE_REQUEST();
|
||||
|
||||
CU_ASSERT(conn->outstanding_requests == 0);
|
||||
free(conn);
|
||||
free(server);
|
||||
}
|
||||
@ -342,6 +311,7 @@ test_parse_request_streaming(void)
|
||||
{
|
||||
struct spdk_jsonrpc_server *server;
|
||||
struct spdk_jsonrpc_server_conn *conn;
|
||||
const char *json_req;
|
||||
size_t len, i;
|
||||
|
||||
server = calloc(1, sizeof(*server));
|
||||
@ -352,6 +322,7 @@ test_parse_request_streaming(void)
|
||||
|
||||
conn->server = server;
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@ -360,6 +331,7 @@ test_parse_request_streaming(void)
|
||||
"{\"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}");
|
||||
|
||||
REQ_BEGIN_VALID();
|
||||
REQ_METHOD("a");
|
||||
REQ_ID_NUM("1");
|
||||
@ -370,22 +342,25 @@ test_parse_request_streaming(void)
|
||||
FREE_REQUEST();
|
||||
|
||||
/* Partial (but not invalid) requests - parse should not consume anything. */
|
||||
snprintf(g_buf, sizeof(g_buf), "%s",
|
||||
"{\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}");
|
||||
len = strlen(g_buf);
|
||||
json_req = " {\"jsonrpc\":\"2.0\",\"method\":\"b\",\"params\":[2],\"id\":2}";
|
||||
len = strlen(json_req);
|
||||
|
||||
/* Try every partial length up to the full request length */
|
||||
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 */
|
||||
CU_ASSERT(rc == 0);
|
||||
CU_ASSERT(g_request == NULL);
|
||||
|
||||
/* In case of faile, don't fload console with ussless CU assert fails. */
|
||||
FREE_REQUEST();
|
||||
}
|
||||
|
||||
/* 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();
|
||||
|
||||
CU_ASSERT(conn->outstanding_requests == 0);
|
||||
free(conn);
|
||||
free(server);
|
||||
}
|
||||
@ -411,7 +386,6 @@ int main(int argc, char **argv)
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||
|
||||
CU_basic_run_tests();
|
||||
@ -419,5 +393,8 @@ int main(int argc, char **argv)
|
||||
num_failures = CU_get_number_of_failures();
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user