1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 #include "spdk/event.h" 36 #include "spdk/util.h" 37 #include "spdk/string.h" 38 #include "spdk/likely.h" 39 #include "spdk/json.h" 40 #include "spdk/endian.h" 41 #include "spdk/bdev.h" 42 #include "spdk/notify.h" 43 #include "spdk/scsi.h" 44 #include "spdk_internal/mock.h" 45 #include "spdk/scsi_spec.h" 46 #include "iscsi/conn.h" 47 #include "iscsi/iscsi.c" 48 #include "scsi/scsi_internal.h" 49 #include "spdk/sock.h" 50 51 #define GET_PDU_LOOP_COUNT 16 52 #define DEFAULT_RUNTIME 30 /* seconds */ 53 #define MAX_RUNTIME_S 86400 /* 24 hours */ 54 55 /* Global run state */ 56 uint64_t g_runtime_ticks; 57 int g_runtime; 58 int g_num_active_threads; 59 bool g_run = true; 60 bool g_is_valid_opcode = true; 61 62 SPDK_LOG_REGISTER_COMPONENT(iscsi) 63 64 /* Global resources */ 65 TAILQ_HEAD(, spdk_iscsi_pdu) g_get_pdu_list; 66 TAILQ_HEAD(, fuzz_iscsi_dev_ctx) g_dev_list = TAILQ_HEAD_INITIALIZER(g_dev_list); 67 struct spdk_poller *g_app_completion_poller; 68 void *g_valid_buffer; 69 unsigned int g_random_seed; 70 char *g_tgt_ip = "127.0.0.1"; 71 char *g_tgt_port = "3260"; 72 /* TBD: Discovery login to get target information. We use fixed IQN for target for now. */ 73 char *g_tgt_name = "iqn.2016-06.io.spdk:disk1"; 74 char *g_init_name = "iqn.2016-06.io.spdk:fuzzinit"; 75 76 struct fuzz_iscsi_iov_ctx { 77 struct iovec iov_req; 78 struct iovec iov_data; 79 struct iovec iov_resp; 80 }; 81 82 struct fuzz_iscsi_io_ctx { 83 struct fuzz_iscsi_iov_ctx iov_ctx; 84 union { 85 struct iscsi_bhs *bhs; 86 struct iscsi_bhs_nop_out *nop_out_req; 87 struct iscsi_bhs_scsi_req *scsi_req; 88 struct iscsi_bhs_task_req *task_req; 89 struct iscsi_bhs_login_req *login_req; 90 struct iscsi_bhs_text_req *text_req; 91 struct iscsi_bhs_data_out *data_out_req; 92 struct iscsi_bhs_logout_req *logout_req; 93 struct iscsi_bhs_snack_req *snack_req; 94 } req; 95 }; 96 97 struct fuzz_iscsi_dev_ctx { 98 struct spdk_iscsi_sess sess; 99 struct spdk_iscsi_conn *conn; 100 struct fuzz_iscsi_io_ctx io_ctx; 101 102 struct spdk_thread *thread; 103 struct spdk_poller *poller; 104 unsigned int random_seed, current_cmd_sn; 105 uint64_t num_sent_pdus; 106 uint64_t num_valid_pdus; 107 108 TAILQ_ENTRY(fuzz_iscsi_dev_ctx) link; 109 }; 110 111 static void 112 fuzz_fill_random_bytes(char *character_repr, size_t len, unsigned int *rand_seed) 113 { 114 size_t i; 115 116 for (i = 0; i < len; i++) { 117 character_repr[i] = rand_r(rand_seed) % UINT8_MAX; 118 } 119 } 120 121 static char * 122 fuzz_get_value_base_64_buffer(void *item, size_t len) 123 { 124 char *value_string; 125 size_t total_size; 126 int rc; 127 128 /* Null pointer */ 129 total_size = spdk_base64_get_encoded_strlen(len) + 1; 130 131 value_string = calloc(1, total_size); 132 if (value_string == NULL) { 133 return NULL; 134 } 135 136 rc = spdk_base64_encode(value_string, item, len); 137 if (rc < 0) { 138 free(value_string); 139 return NULL; 140 } 141 142 return value_string; 143 } 144 145 int 146 iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser, 147 int ag_tag) 148 { 149 return 0; 150 } 151 152 void 153 shutdown_iscsi_conns_done(void) 154 { 155 return; 156 } 157 158 void 159 iscsi_put_pdu(struct spdk_iscsi_pdu *pdu) 160 { 161 if (!pdu) { 162 return; 163 } 164 165 pdu->ref--; 166 if (pdu->ref < 0) { 167 pdu->ref = 0; 168 } 169 170 if (pdu->ref == 0) { 171 if (pdu->data) { 172 free(pdu->data); 173 } 174 free(pdu); 175 } 176 } 177 178 struct spdk_iscsi_pdu * 179 iscsi_get_pdu(struct spdk_iscsi_conn *conn) 180 { 181 struct spdk_iscsi_pdu *pdu; 182 183 pdu = calloc(1, sizeof(*pdu)); 184 if (!pdu) { 185 return NULL; 186 } 187 188 pdu->ref = 1; 189 pdu->conn = conn; 190 191 return pdu; 192 } 193 194 static void 195 iscsi_task_free(struct spdk_scsi_task *scsi_task) 196 { 197 struct spdk_iscsi_task *task = iscsi_task_from_scsi_task(scsi_task); 198 199 assert(task->parent == NULL); 200 201 iscsi_task_disassociate_pdu(task); 202 assert(task->conn->pending_task_cnt > 0); 203 task->conn->pending_task_cnt--; 204 free(task); 205 } 206 207 struct spdk_iscsi_task * 208 iscsi_task_get(struct spdk_iscsi_conn *conn, struct spdk_iscsi_task *parent, 209 spdk_scsi_task_cpl cpl_fn) 210 { 211 struct spdk_iscsi_task *task; 212 213 /* iSCSI subtask is not necessary for now. */ 214 assert(parent == NULL); 215 216 task = calloc(1, sizeof(*task)); 217 if (!task) { 218 printf("Unable to get task\n"); 219 abort(); 220 } 221 222 task->conn = conn; 223 assert(conn->pending_task_cnt < UINT32_MAX); 224 conn->pending_task_cnt++; 225 spdk_scsi_task_construct(&task->scsi, cpl_fn, iscsi_task_free); 226 227 return task; 228 } 229 230 static void 231 cleanup(void) 232 { 233 struct fuzz_iscsi_dev_ctx *dev_ctx, *tmp; 234 235 TAILQ_FOREACH_SAFE(dev_ctx, &g_dev_list, link, tmp) { 236 printf("device %p stats: Sent %lu valid opcode PDUs, %lu invalid opcode PDUs.\n", 237 dev_ctx, dev_ctx->num_valid_pdus, 238 dev_ctx->num_sent_pdus - dev_ctx->num_valid_pdus); 239 free(dev_ctx); 240 } 241 242 spdk_free(g_valid_buffer); 243 } 244 245 /* data dumping functions begin */ 246 static int 247 dump_iscsi_cmd(void *ctx, const void *data, size_t size) 248 { 249 fprintf(stderr, "%s\n", (const char *)data); 250 return 0; 251 } 252 253 static void 254 print_scsi_io_data(struct spdk_json_write_ctx *w, struct fuzz_iscsi_io_ctx *io_ctx) 255 { 256 char *data_segment_len; 257 258 data_segment_len = fuzz_get_value_base_64_buffer((void *)io_ctx->req.bhs->data_segment_len, 259 sizeof(io_ctx->req.bhs->data_segment_len)); 260 261 spdk_json_write_named_uint32(w, "opcode", io_ctx->req.bhs->opcode); 262 spdk_json_write_named_uint32(w, "immediate", io_ctx->req.bhs->immediate); 263 spdk_json_write_named_uint32(w, "reserved", io_ctx->req.bhs->reserved); 264 spdk_json_write_named_uint32(w, "total_ahs_len", io_ctx->req.bhs->total_ahs_len); 265 spdk_json_write_named_string(w, "data_segment_len", data_segment_len); 266 spdk_json_write_named_uint32(w, "itt", io_ctx->req.bhs->itt); 267 spdk_json_write_named_uint32(w, "exp_stat_sn", io_ctx->req.bhs->exp_stat_sn); 268 269 free(data_segment_len); 270 } 271 272 static void 273 print_req_obj(struct fuzz_iscsi_dev_ctx *dev_ctx, struct fuzz_iscsi_io_ctx *io_ctx) 274 { 275 struct spdk_json_write_ctx *w; 276 277 w = spdk_json_write_begin(dump_iscsi_cmd, NULL, SPDK_JSON_WRITE_FLAG_FORMATTED); 278 spdk_json_write_named_object_begin(w, "bhs"); 279 print_scsi_io_data(w, io_ctx); 280 spdk_json_write_object_end(w); 281 spdk_json_write_end(w); 282 } 283 284 /* data dumping functions end */ 285 286 /* dev initialization begin */ 287 static int 288 fuzz_iscsi_dev_init(void) 289 { 290 struct fuzz_iscsi_dev_ctx *dev_ctx; 291 int rc = 0; 292 293 dev_ctx = calloc(1, sizeof(*dev_ctx)); 294 if (dev_ctx == NULL) { 295 return -ENOMEM; 296 } 297 298 dev_ctx->thread = spdk_get_thread(); 299 if (dev_ctx->thread == NULL) { 300 fprintf(stderr, "Unable to get a thread for a fuzz device.\n"); 301 rc = -EINVAL; 302 goto error_out; 303 } 304 305 dev_ctx->current_cmd_sn = 0; 306 307 TAILQ_INSERT_TAIL(&g_dev_list, dev_ctx, link); 308 return 0; 309 310 error_out: 311 free(dev_ctx); 312 return rc; 313 } 314 /* dev initialization end */ 315 316 /* build requests begin */ 317 static void 318 prep_iscsi_pdu_bhs_opcode_cmd(struct fuzz_iscsi_dev_ctx *dev_ctx, struct fuzz_iscsi_io_ctx *io_ctx) 319 { 320 io_ctx->iov_ctx.iov_req.iov_len = sizeof(struct iscsi_bhs); 321 fuzz_fill_random_bytes((char *)io_ctx->req.bhs, sizeof(struct iscsi_bhs), 322 &dev_ctx->random_seed); 323 } 324 /* build requests end */ 325 326 static int 327 iscsi_pdu_hdr_op_login_rsp(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) 328 { 329 return 0; 330 } 331 332 static int 333 iscsi_fuzz_pdu_hdr_handle(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) 334 { 335 int opcode; 336 int rc = 0; 337 338 opcode = pdu->bhs.opcode; 339 if (opcode == ISCSI_OP_LOGIN_RSP) { 340 return iscsi_pdu_hdr_op_login_rsp(conn, pdu); 341 } 342 343 switch (opcode) { 344 case ISCSI_OP_LOGOUT_RSP: 345 fprintf(stderr, "Received logout hdr_handle response opcode(0x26) from Target.\n"); 346 conn->is_logged_out = true; 347 break; 348 case ISCSI_OP_NOPIN: 349 case ISCSI_OP_SCSI_RSP: 350 case ISCSI_OP_TASK_RSP: 351 case ISCSI_OP_TEXT_RSP: 352 case ISCSI_OP_SCSI_DATAIN: 353 case ISCSI_OP_R2T: 354 case ISCSI_OP_ASYNC: 355 case ISCSI_OP_VENDOR_3C: 356 case ISCSI_OP_VENDOR_3D: 357 case ISCSI_OP_VENDOR_3E: 358 fprintf(stderr, "Received hdr_handle response opcode from Target is 0x%x.\n", pdu->bhs.opcode); 359 break; 360 case ISCSI_OP_REJECT: 361 fprintf(stderr, "Received rejected hdr_handle response opcode(0x3f) from Target.\n"); 362 break; 363 default: 364 rc = -1; 365 break; 366 } 367 368 return rc; 369 } 370 371 static int 372 iscsi_pdu_payload_op_login_rsp(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) 373 { 374 struct iscsi_bhs_login_rsp *rsph; 375 376 rsph = (struct iscsi_bhs_login_rsp *)&pdu->bhs; 377 if (rsph == NULL) { 378 return -1; 379 } 380 381 assert(rsph->tsih != 0); 382 assert(rsph->status_class == 0); 383 assert(ISCSI_BHS_LOGIN_GET_TBIT(rsph->flags)); 384 assert(!(rsph->flags & ISCSI_LOGIN_CONTINUE)); 385 assert((rsph->flags & ISCSI_LOGIN_NEXT_STAGE_MASK) == ISCSI_LOGIN_NEXT_STAGE_3); 386 387 /* We got the Login Final Response and move to Full-Feature Phase. */ 388 conn->full_feature = 1; 389 return 0; 390 } 391 392 static int 393 iscsi_fuzz_pdu_payload_handle(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) 394 { 395 int opcode; 396 int rc = 0; 397 398 opcode = pdu->bhs.opcode; 399 fprintf(stderr, "Received payload_handle response opcode from Target is 0x%x.\n", opcode); 400 401 switch (opcode) { 402 case ISCSI_OP_LOGIN_RSP: 403 rc = iscsi_pdu_payload_op_login_rsp(conn, pdu); 404 break; 405 case ISCSI_OP_NOPIN: 406 case ISCSI_OP_SCSI_RSP: 407 case ISCSI_OP_TASK_RSP: 408 case ISCSI_OP_TEXT_RSP: 409 case ISCSI_OP_SCSI_DATAIN: 410 case ISCSI_OP_R2T: 411 case ISCSI_OP_ASYNC: 412 case ISCSI_OP_VENDOR_3C: 413 case ISCSI_OP_VENDOR_3D: 414 case ISCSI_OP_VENDOR_3E: 415 case ISCSI_OP_REJECT: 416 break; 417 default: 418 rc = -1; 419 break; 420 } 421 422 return rc; 423 } 424 425 static int 426 iscsi_fuzz_read_pdu(struct spdk_iscsi_conn *conn) 427 { 428 enum iscsi_pdu_recv_state prev_state; 429 struct spdk_iscsi_pdu *pdu; 430 uint32_t data_len; 431 int rc; 432 433 do { 434 prev_state = conn->pdu_recv_state; 435 pdu = conn->pdu_in_progress; 436 437 switch (conn->pdu_recv_state) { 438 case ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY: 439 assert(conn->pdu_in_progress == NULL); 440 441 conn->pdu_in_progress = iscsi_get_pdu(conn); 442 if (conn->pdu_in_progress == NULL) { 443 return SPDK_ISCSI_CONNECTION_FATAL; 444 } 445 TAILQ_INSERT_TAIL(&g_get_pdu_list, conn->pdu_in_progress, tailq); 446 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR; 447 break; 448 case ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR: 449 if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) { 450 rc = iscsi_conn_read_data(conn, 451 ISCSI_BHS_LEN - pdu->bhs_valid_bytes, 452 (uint8_t *)&pdu->bhs + pdu->bhs_valid_bytes); 453 if (rc < 0) { 454 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR; 455 break; 456 } 457 pdu->bhs_valid_bytes += rc; 458 if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) { 459 return 0; 460 } 461 } 462 463 pdu->data_segment_len = ISCSI_ALIGN(DGET24(pdu->bhs.data_segment_len)); 464 465 rc = iscsi_fuzz_pdu_hdr_handle(conn, pdu); 466 if (rc < 0) { 467 printf("Critical error is detected. Close the connection\n"); 468 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR; 469 break; 470 } 471 472 if (conn->is_logged_out) { 473 printf("pdu received after logout\n"); 474 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR; 475 break; 476 } 477 478 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD; 479 break; 480 case ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD: 481 data_len = pdu->data_segment_len; 482 if (data_len != 0 && pdu->data_buf == NULL) { 483 pdu->data_buf = calloc(1, data_len); 484 if (pdu->data_buf == NULL) { 485 return 0; 486 } 487 pdu->data = pdu->data_buf; 488 } 489 490 /* copy the actual data into local buffer */ 491 if (pdu->data_valid_bytes < data_len) { 492 rc = iscsi_conn_read_data_segment(conn, pdu, data_len); 493 if (rc < 0) { 494 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR; 495 break; 496 } 497 pdu->data_valid_bytes += rc; 498 if (pdu->data_valid_bytes < data_len) { 499 return 0; 500 } 501 } 502 503 /* All data for this PDU has now been read from the socket. */ 504 spdk_trace_record(TRACE_ISCSI_READ_PDU, conn->id, pdu->data_valid_bytes, 505 (uintptr_t)pdu, pdu->bhs.opcode); 506 507 if (!pdu->is_rejected) { 508 rc = iscsi_fuzz_pdu_payload_handle(conn, pdu); 509 } else { 510 rc = 0; 511 } 512 if (rc == 0) { 513 spdk_trace_record(TRACE_ISCSI_TASK_EXECUTED, 0, 0, (uintptr_t)pdu, 0); 514 conn->pdu_in_progress = NULL; 515 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY; 516 return 1; 517 } else { 518 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR; 519 } 520 break; 521 case ISCSI_PDU_RECV_STATE_ERROR: 522 return SPDK_ISCSI_CONNECTION_FATAL; 523 default: 524 assert(false); 525 printf("code should not come here\n"); 526 break; 527 } 528 } while (prev_state != conn->pdu_recv_state); 529 530 return 0; 531 } 532 533 #define GET_PDU_LOOP_COUNT 16 534 535 static int 536 fuzz_iscsi_handle_incoming_pdus(struct spdk_iscsi_conn *conn) 537 { 538 int i, rc; 539 540 /* Read new PDUs from network */ 541 for (i = 0; i < GET_PDU_LOOP_COUNT; i++) { 542 rc = iscsi_fuzz_read_pdu(conn); 543 if (rc == 0) { 544 break; 545 } else if (rc < 0) { 546 return rc; 547 } 548 } 549 550 return i; 551 } 552 553 static void 554 fuzz_iscsi_send_login_request(struct fuzz_iscsi_dev_ctx *dev_ctx, uint8_t session_type) 555 { 556 struct fuzz_iscsi_io_ctx *io_ctx = NULL; 557 struct spdk_iscsi_pdu *req_pdu; 558 struct iscsi_bhs_login_req *login_req; 559 struct spdk_iscsi_conn *conn = dev_ctx->conn; 560 561 req_pdu = iscsi_get_pdu(conn); 562 req_pdu->writev_offset = 0; 563 req_pdu->hdigest_valid_bytes = 0; 564 req_pdu->ahs_valid_bytes = 0; 565 req_pdu->data_buf_len = 8192; 566 req_pdu->data = calloc(1, 8192); 567 assert(req_pdu->data != NULL); 568 req_pdu->data_segment_len = 0; 569 570 login_req = (struct iscsi_bhs_login_req *)&req_pdu->bhs; 571 io_ctx = &dev_ctx->io_ctx; 572 io_ctx->req.login_req = login_req; 573 io_ctx->req.login_req->version_min = 0; 574 /* a new session */ 575 io_ctx->req.login_req->tsih = 0; 576 577 req_pdu->bhs.opcode = ISCSI_OP_LOGIN; 578 req_pdu->bhs.immediate = 1; 579 req_pdu->bhs.reserved = 0; 580 req_pdu->bhs_valid_bytes = ISCSI_BHS_LEN; 581 req_pdu->bhs.total_ahs_len = 0; 582 583 /* An initiator that chooses to operate without iSCSI security and with 584 * all the operational parameters taking the default values issues the 585 * Login with the T bit set to 1, the CSG set to 586 * LoginOperationalNegotiation, and the NSG set to FullFeaturePhase. 587 * 588 * Byte / 0 | 1 | 2 | 3 | 589 * |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7| 590 * +---------------+---------------+---------------+---------------+ 591 * 0|.|1| 0x03 |T|C|.|.|CSG|NSG| Version-max | Version-min | 592 */ 593 req_pdu->bhs.flags = ISCSI_LOGIN_TRANSIT | (ISCSI_OPERATIONAL_NEGOTIATION_PHASE << 2) | 594 ISCSI_FULL_FEATURE_PHASE; 595 596 req_pdu->data_segment_len = iscsi_append_text("InitiatorName", g_init_name, 597 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 598 req_pdu->data_segment_len = iscsi_append_text("HeaderDigest", "None", 599 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 600 req_pdu->data_segment_len = iscsi_append_text("DataDigest", "None", 601 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 602 req_pdu->data_segment_len = iscsi_append_text("DefaultTime2Wait", "2", 603 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 604 req_pdu->data_segment_len = iscsi_append_text("DefaultTime2Retain", "0", 605 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 606 req_pdu->data_segment_len = iscsi_append_text("IFMarker", "No", 607 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 608 req_pdu->data_segment_len = iscsi_append_text("OFMarker", "No", 609 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 610 req_pdu->data_segment_len = iscsi_append_text("ErrorRecoveryLevel", "0", 611 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 612 613 if (session_type == SESSION_TYPE_DISCOVERY) { 614 /* Discovery PDU */ 615 conn->sess->session_type = SESSION_TYPE_DISCOVERY; 616 req_pdu->data_segment_len = iscsi_append_text("SessionType", "Discovery", 617 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 618 req_pdu->data_segment_len = iscsi_append_text("MaxRecvDataSegmentLength", "32768", 619 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 620 } else { 621 /* Login PDU */ 622 conn->sess->session_type = SESSION_TYPE_NORMAL; 623 req_pdu->data_segment_len = iscsi_append_text("SessionType", "Normal", 624 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 625 req_pdu->data_segment_len = iscsi_append_text("TargetName", g_tgt_name, 626 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 627 req_pdu->data_segment_len = iscsi_append_text("InitialR2T", "No", 628 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 629 req_pdu->data_segment_len = iscsi_append_text("ImmediateData", "Yes", 630 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 631 req_pdu->data_segment_len = iscsi_append_text("MaxBurstLength", "16776192", 632 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 633 req_pdu->data_segment_len = iscsi_append_text("FirstBurstLength", "262144", 634 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 635 req_pdu->data_segment_len = iscsi_append_text("MaxOutstandingR2T", "1", 636 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 637 req_pdu->data_segment_len = iscsi_append_text("MaxConnections", "1", 638 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 639 req_pdu->data_segment_len = iscsi_append_text("DataPDUInOrder", "Yes", 640 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 641 req_pdu->data_segment_len = iscsi_append_text("DataSequenceInOrder", "Yes", 642 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 643 req_pdu->data_segment_len = iscsi_append_text("MaxRecvDataSegmentLength", "262144", 644 req_pdu->data, req_pdu->data_buf_len, req_pdu->data_segment_len); 645 } 646 647 DSET24(req_pdu->bhs.data_segment_len, req_pdu->data_segment_len); 648 iscsi_conn_write_pdu(conn, req_pdu, iscsi_conn_pdu_generic_complete, NULL); 649 } 650 651 static void 652 fuzz_iscsi_send_logout_request(struct fuzz_iscsi_dev_ctx *dev_ctx) 653 { 654 struct fuzz_iscsi_io_ctx *io_ctx = NULL; 655 struct spdk_iscsi_pdu *req_pdu; 656 struct iscsi_bhs_logout_req *logout_req; 657 struct spdk_iscsi_conn *conn = dev_ctx->conn; 658 659 conn->is_logged_out = true; 660 661 req_pdu = iscsi_get_pdu(conn); 662 req_pdu->writev_offset = 0; 663 req_pdu->hdigest_valid_bytes = 0; 664 req_pdu->ahs_valid_bytes = 0; 665 req_pdu->data_buf_len = 0; 666 667 logout_req = (struct iscsi_bhs_logout_req *)&req_pdu->bhs; 668 io_ctx = &dev_ctx->io_ctx; 669 io_ctx->req.logout_req = logout_req; 670 671 req_pdu->bhs.opcode = ISCSI_OP_LOGOUT; 672 req_pdu->bhs.immediate = 1; 673 req_pdu->bhs.reserved = 0; 674 req_pdu->bhs_valid_bytes = ISCSI_BHS_LEN; 675 req_pdu->bhs.total_ahs_len = 0; 676 req_pdu->bhs.flags = 0; 677 678 DSET24(req_pdu->bhs.data_segment_len, 0); 679 iscsi_conn_write_pdu(conn, req_pdu, iscsi_conn_pdu_generic_complete, conn); 680 } 681 682 static void 683 iscsi_fuzz_conn_reset(struct spdk_iscsi_conn *conn, struct spdk_iscsi_sess *sess) 684 { 685 conn->sess = sess; 686 conn->data_in_cnt = 0; 687 conn->params = NULL; 688 conn->header_digest = true; 689 conn->data_digest = false; 690 conn->header_digest = 0; 691 conn->MaxRecvDataSegmentLength = 8192; 692 conn->full_feature = 0; 693 conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY; 694 conn->pdu_in_progress = NULL; 695 conn->is_logged_out = 0; 696 } 697 698 static void 699 iscsi_fuzz_sock_connect(struct spdk_iscsi_conn *conn) 700 { 701 const char *host = g_tgt_ip; 702 const char *port = g_tgt_port; 703 char saddr[INET6_ADDRSTRLEN], caddr[INET6_ADDRSTRLEN]; 704 uint16_t cport, sport; 705 int rc = 0; 706 707 conn->sock = spdk_sock_connect(host, spdk_strtol(port, 10), NULL); 708 if (conn->sock == NULL) { 709 fprintf(stderr, "connect error(%d): %s\n", errno, spdk_strerror(errno)); 710 spdk_sock_close(&conn->sock); 711 return; 712 } 713 fprintf(stderr, "\nConnecting to the server on %s:%s\n", host, port); 714 715 rc = spdk_sock_getaddr(conn->sock, saddr, sizeof(saddr), &sport, caddr, sizeof(caddr), &cport); 716 if (rc < 0) { 717 fprintf(stderr, "Cannot get connection addresses\n"); 718 spdk_sock_close(&conn->sock); 719 return; 720 } 721 722 fprintf(stderr, "Connection accepted from (%s, %hu) to (%s, %hu)\n", caddr, cport, saddr, sport); 723 724 } 725 726 static void 727 check_successful_op(struct fuzz_iscsi_dev_ctx *dev_ctx, struct fuzz_iscsi_io_ctx *io_ctx) 728 { 729 if (g_is_valid_opcode) { 730 fprintf(stderr, "Sent a valid opcode PDU.\n"); 731 dev_ctx->num_valid_pdus++; 732 } else { 733 fprintf(stderr, "Sent an invalid opcode PDU.\n"); 734 } 735 } 736 737 /* submit requests begin */ 738 static void 739 dev_submit_requests(struct fuzz_iscsi_dev_ctx *dev_ctx) 740 { 741 struct fuzz_iscsi_io_ctx *io_ctx = NULL; 742 uint8_t opcode; 743 struct spdk_iscsi_pdu *req_pdu; 744 struct iscsi_bhs *bhs; 745 struct iscsi_bhs_nop_out *nop_out_req; 746 struct iscsi_bhs_scsi_req *scsi_req; 747 struct iscsi_bhs_task_req *task_req; 748 struct iscsi_bhs_text_req *text_req; 749 struct iscsi_bhs_data_out *data_out_req; 750 struct iscsi_bhs_snack_req *snack_req; 751 unsigned int rand_seed; 752 bool is_p99; 753 754 g_is_valid_opcode = true; 755 756 /* Random PDU */ 757 opcode = rand() % 0x3f; 758 fprintf(stderr, "Random request bhs.opcode of Initiator is 0x%x.\n", opcode); 759 760 if ((opcode == ISCSI_OP_LOGIN) || (opcode == ISCSI_OP_LOGOUT)) { 761 /* only need send next */ 762 fprintf(stderr, "LOGIN and LOGOUT opcodes are ignored here.\n"); 763 return; 764 } 765 766 req_pdu = iscsi_get_pdu(dev_ctx->conn); 767 req_pdu->writev_offset = 0; 768 req_pdu->hdigest_valid_bytes = 0; 769 req_pdu->ahs_valid_bytes = 0; 770 req_pdu->data_buf_len = 0; 771 772 dev_ctx->conn->sess->session_type = SESSION_TYPE_NORMAL; 773 774 io_ctx = &dev_ctx->io_ctx; 775 776 switch (opcode) { 777 case ISCSI_OP_NOPOUT: 778 nop_out_req = (struct iscsi_bhs_nop_out *)&req_pdu->bhs; 779 io_ctx->req.nop_out_req = nop_out_req; 780 break; 781 case ISCSI_OP_SCSI: 782 scsi_req = (struct iscsi_bhs_scsi_req *)&req_pdu->bhs; 783 io_ctx->req.scsi_req = scsi_req; 784 break; 785 case ISCSI_OP_TASK: 786 task_req = (struct iscsi_bhs_task_req *)&req_pdu->bhs; 787 io_ctx->req.task_req = task_req; 788 break; 789 case ISCSI_OP_TEXT: 790 text_req = (struct iscsi_bhs_text_req *)&req_pdu->bhs; 791 io_ctx->req.text_req = text_req; 792 break; 793 case ISCSI_OP_SCSI_DATAOUT: 794 data_out_req = (struct iscsi_bhs_data_out *)&req_pdu->bhs; 795 io_ctx->req.data_out_req = data_out_req; 796 break; 797 case ISCSI_OP_SNACK: 798 snack_req = (struct iscsi_bhs_snack_req *)&req_pdu->bhs; 799 io_ctx->req.snack_req = snack_req; 800 break; 801 default: 802 bhs = (struct iscsi_bhs *)&req_pdu->bhs; 803 io_ctx->req.bhs = bhs; 804 g_is_valid_opcode = false; 805 break; 806 } 807 808 prep_iscsi_pdu_bhs_opcode_cmd(dev_ctx, io_ctx); 809 io_ctx->req.bhs->opcode = opcode; 810 req_pdu->bhs.opcode = opcode; 811 req_pdu->bhs.immediate = 1; 812 req_pdu->bhs.reserved = 0; 813 req_pdu->bhs_valid_bytes = ISCSI_BHS_LEN; 814 req_pdu->bhs.total_ahs_len = 0; 815 req_pdu->bhs.stat_sn = 0; 816 DSET24(req_pdu->bhs.data_segment_len, 0); 817 818 if (opcode <= ISCSI_OP_TEXT) { 819 rand_seed = time(NULL); 820 is_p99 = rand_r(&rand_seed) % 100 == 0 ? true : false; 821 if (!is_p99) { /* Remaining 1% */ 822 switch (opcode) { 823 case ISCSI_OP_NOPOUT: 824 if (req_pdu->bhs.immediate) { 825 io_ctx->req.nop_out_req->cmd_sn = dev_ctx->current_cmd_sn; 826 } else { 827 io_ctx->req.nop_out_req->cmd_sn = dev_ctx->current_cmd_sn++; 828 } 829 break; 830 case ISCSI_OP_SCSI: 831 if (req_pdu->bhs.immediate) { 832 io_ctx->req.scsi_req->cmd_sn = dev_ctx->current_cmd_sn; 833 } else { 834 io_ctx->req.scsi_req->cmd_sn = dev_ctx->current_cmd_sn++; 835 } 836 break; 837 case ISCSI_OP_TASK: 838 if (req_pdu->bhs.immediate) { 839 io_ctx->req.task_req->cmd_sn = dev_ctx->current_cmd_sn; 840 } else { 841 io_ctx->req.task_req->cmd_sn = dev_ctx->current_cmd_sn++; 842 } 843 break; 844 case ISCSI_OP_TEXT: 845 if (req_pdu->bhs.immediate) { 846 io_ctx->req.text_req->cmd_sn = dev_ctx->current_cmd_sn; 847 } else { 848 io_ctx->req.text_req->cmd_sn = dev_ctx->current_cmd_sn++; 849 } 850 break; 851 default: 852 break; 853 } 854 } 855 } 856 857 if (opcode == ISCSI_OP_SCSI) { 858 /* avoid ((R_bit != 0) && (W_bit != 0)) is true */ 859 io_ctx->req.scsi_req->read_bit = 0; 860 io_ctx->req.scsi_req->write_bit = 0; 861 } 862 863 if (opcode == ISCSI_OP_TEXT) { 864 /* avoid: (F_bit && C_bit) is true */ 865 io_ctx->req.text_req->flags = 0; 866 /* avoid: correct itt is not equal to the current itt */ 867 io_ctx->req.text_req->itt = 0; 868 } 869 870 fprintf(stderr, "Dumping this request bhs contents now.\n"); 871 print_req_obj(dev_ctx, io_ctx); 872 873 check_successful_op(dev_ctx, io_ctx); 874 dev_ctx->num_sent_pdus++; 875 876 iscsi_conn_write_pdu(dev_ctx->conn, req_pdu, 877 iscsi_conn_pdu_generic_complete, NULL); 878 } 879 /* submit requests end */ 880 881 static int 882 poll_dev(void *ctx) 883 { 884 struct fuzz_iscsi_dev_ctx *dev_ctx = ctx; 885 struct spdk_iscsi_conn *conn = dev_ctx->conn; 886 uint64_t current_ticks; 887 struct spdk_iscsi_pdu *pdu, *tmp; 888 889 current_ticks = spdk_get_ticks(); 890 if (current_ticks > g_runtime_ticks) { 891 g_run = false; 892 } 893 894 if (!g_run) { 895 /* Logout PDU */ 896 fuzz_iscsi_send_logout_request(dev_ctx); 897 fuzz_iscsi_handle_incoming_pdus(conn); 898 899 TAILQ_FOREACH_SAFE(pdu, &g_get_pdu_list, tailq, tmp) { 900 TAILQ_REMOVE(&g_get_pdu_list, pdu, tailq); 901 iscsi_put_pdu(pdu); 902 } 903 904 spdk_sock_close(&conn->sock); 905 906 TAILQ_FOREACH_SAFE(pdu, &conn->write_pdu_list, tailq, tmp) { 907 TAILQ_REMOVE(&conn->write_pdu_list, pdu, tailq); 908 iscsi_put_pdu(pdu); 909 } 910 911 free(conn); 912 913 spdk_poller_unregister(&dev_ctx->poller); 914 __sync_sub_and_fetch(&g_num_active_threads, 1); 915 916 return -1; 917 } 918 919 if (conn->is_logged_out) { 920 spdk_sock_close(&conn->sock); 921 iscsi_fuzz_conn_reset(conn, &dev_ctx->sess); 922 iscsi_fuzz_sock_connect(conn); 923 usleep(1000); 924 925 /* Login PDU */ 926 fuzz_iscsi_send_login_request(dev_ctx, SESSION_TYPE_NORMAL); 927 } else if (conn->full_feature == 1) { 928 dev_submit_requests(dev_ctx); 929 } 930 931 spdk_sock_flush(conn->sock); 932 933 fuzz_iscsi_handle_incoming_pdus(conn); 934 935 return 0; 936 } 937 938 static void 939 start_io(void *ctx) 940 { 941 struct fuzz_iscsi_dev_ctx *dev_ctx = ctx; 942 943 dev_ctx->sess.ExpCmdSN = 0; 944 dev_ctx->sess.MaxCmdSN = 64; 945 dev_ctx->sess.MaxBurstLength = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; 946 dev_ctx->sess.MaxOutstandingR2T = 1; 947 dev_ctx->sess.tag = 1; 948 dev_ctx->sess.tsih = 256; 949 950 dev_ctx->conn = calloc(1, sizeof(*dev_ctx->conn)); 951 assert(dev_ctx->conn != NULL); 952 TAILQ_INIT(&dev_ctx->conn->write_pdu_list); 953 954 iscsi_fuzz_conn_reset(dev_ctx->conn, &dev_ctx->sess); 955 iscsi_fuzz_sock_connect(dev_ctx->conn); 956 usleep(1000); 957 958 /* Login PDU */ 959 fuzz_iscsi_send_login_request(dev_ctx, SESSION_TYPE_NORMAL); 960 961 if (g_random_seed) { 962 dev_ctx->random_seed = g_random_seed; 963 } else { 964 dev_ctx->random_seed = spdk_get_ticks(); 965 } 966 967 dev_ctx->poller = SPDK_POLLER_REGISTER(poll_dev, dev_ctx, 0); 968 if (dev_ctx->poller == NULL) { 969 return; 970 } 971 } 972 973 static int 974 check_app_completion(void *ctx) 975 { 976 if (g_num_active_threads == 0) { 977 spdk_poller_unregister(&g_app_completion_poller); 978 printf("Fuzzing completed. Shutting down the fuzz application.\n\n"); 979 cleanup(); 980 spdk_app_stop(0); 981 } 982 return 0; 983 } 984 985 static void 986 begin_iscsi_fuzz(void *ctx) 987 { 988 struct fuzz_iscsi_dev_ctx *dev_ctx; 989 int rc; 990 991 g_runtime_ticks = spdk_get_ticks() + g_runtime * spdk_get_ticks_hz(); 992 993 g_valid_buffer = spdk_malloc(0x1000, 0x200, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_SHARE); 994 if (g_valid_buffer == NULL) { 995 fprintf(stderr, "Failed to allocate a valid buffer for random PDUs\n"); 996 goto out; 997 } 998 999 rc = fuzz_iscsi_dev_init(); 1000 if (rc) { 1001 fprintf(stderr, "fuzz_iscsi_dev_init() failed.\n"); 1002 goto out; 1003 } 1004 1005 TAILQ_FOREACH(dev_ctx, &g_dev_list, link) { 1006 assert(dev_ctx->thread != NULL); 1007 spdk_thread_send_msg(dev_ctx->thread, start_io, dev_ctx); 1008 __sync_add_and_fetch(&g_num_active_threads, 1); 1009 } 1010 1011 g_app_completion_poller = SPDK_POLLER_REGISTER(check_app_completion, NULL, 1000000); 1012 if (g_app_completion_poller == NULL) { 1013 fprintf(stderr, "Failed to register a poller for test completion checking.\n"); 1014 goto out; 1015 } 1016 1017 return; 1018 out: 1019 cleanup(); 1020 spdk_app_stop(0); 1021 } 1022 1023 static void 1024 iscsi_fuzz_usage(void) 1025 { 1026 fprintf(stderr, " -T <path> iSCSI Target IP address.\n"); 1027 fprintf(stderr, " -S <integer> Seed value for test.\n"); 1028 fprintf(stderr, 1029 " -t <integer> Time in seconds to run the fuzz test. Only valid if -j is not specified.\n"); 1030 } 1031 1032 static int 1033 iscsi_fuzz_parse(int ch, char *arg) 1034 { 1035 int64_t error_test; 1036 1037 switch (ch) { 1038 case 'T': 1039 g_tgt_ip = optarg; 1040 break; 1041 case 'S': 1042 error_test = spdk_strtol(arg, 10); 1043 if (error_test < 0) { 1044 fprintf(stderr, "Invalid value supplied for the random seed.\n"); 1045 return -1; 1046 } else { 1047 g_random_seed = error_test; 1048 } 1049 break; 1050 case 't': 1051 g_runtime = spdk_strtol(optarg, 10); 1052 if (g_runtime <= 0 || g_runtime > MAX_RUNTIME_S) { 1053 fprintf(stderr, "You must supply a positive runtime value less than %d.\n", MAX_RUNTIME_S); 1054 return -1; 1055 } 1056 break; 1057 case '?': 1058 default: 1059 iscsi_fuzz_usage(); 1060 return -EINVAL; 1061 } 1062 1063 return 0; 1064 } 1065 1066 int 1067 main(int argc, char **argv) 1068 { 1069 struct spdk_app_opts opts = {}; 1070 int rc; 1071 1072 g_runtime = DEFAULT_RUNTIME; 1073 srand((unsigned)time(0)); 1074 1075 TAILQ_INIT(&g_get_pdu_list); 1076 1077 spdk_app_opts_init(&opts); 1078 opts.name = "iscsi_fuzz"; 1079 1080 if ((rc = spdk_app_parse_args(argc, argv, &opts, "T:S:t:", NULL, iscsi_fuzz_parse, 1081 iscsi_fuzz_usage) != SPDK_APP_PARSE_ARGS_SUCCESS)) { 1082 return rc; 1083 } 1084 1085 rc = spdk_app_start(&opts, begin_iscsi_fuzz, NULL); 1086 1087 return rc; 1088 } 1089