Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

iscsi-target: Fix wrong buffer / buffer overrun in iscsi_change_param_value()

In non-leading connection login, iscsi_login_non_zero_tsih_s1() calls
iscsi_change_param_value() with the buffer it uses to hold the login
PDU, not a temporary buffer. This leads to the login header getting
corrupted and login failing for non-leading connections in MC/S.

Fix this by adding a wrapper iscsi_change_param_sprintf() that handles
the temporary buffer itself to avoid confusion. Also handle sending a
reject in case of failure in the wrapper, which lets the calling code
get quite a bit smaller and easier to read.

Finally, bump the size of the temporary buffer from 32 to 64 bytes to be
safe, since "MaxRecvDataSegmentLength=" by itself is 25 bytes; with a
trailing NUL, a value >= 1M will lead to a buffer overrun. (This isn't
the default but we don't need to run right at the ragged edge here)

Reported-by: Santosh Kulkarni <santosh.kulkarni@calsoftinc.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Cc: stable@vger.kernel.org # 3.10+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>

authored by

Roland Dreier and committed by
Nicholas Bellinger
79d59d08 6cc44a6f

+31 -39
+31 -39
drivers/target/iscsi/iscsi_target_login.c
··· 249 249 mutex_unlock(&auth_id_lock); 250 250 } 251 251 252 + static __printf(2, 3) int iscsi_change_param_sprintf( 253 + struct iscsi_conn *conn, 254 + const char *fmt, ...) 255 + { 256 + va_list args; 257 + unsigned char buf[64]; 258 + 259 + memset(buf, 0, sizeof buf); 260 + 261 + va_start(args, fmt); 262 + vsnprintf(buf, sizeof buf, fmt, args); 263 + va_end(args); 264 + 265 + if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 266 + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 267 + ISCSI_LOGIN_STATUS_NO_RESOURCES); 268 + return -1; 269 + } 270 + 271 + return 0; 272 + } 273 + 252 274 /* 253 275 * This is the leading connection of a new session, 254 276 * or session reinstatement. ··· 361 339 { 362 340 struct iscsi_node_attrib *na; 363 341 struct iscsi_session *sess = conn->sess; 364 - unsigned char buf[32]; 365 342 bool iser = false; 366 343 367 344 sess->tpg = conn->tpg; ··· 401 380 * 402 381 * In our case, we have already located the struct iscsi_tiqn at this point. 403 382 */ 404 - memset(buf, 0, 32); 405 - sprintf(buf, "TargetPortalGroupTag=%hu", sess->tpg->tpgt); 406 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 407 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 408 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 383 + if (iscsi_change_param_sprintf(conn, "TargetPortalGroupTag=%hu", sess->tpg->tpgt)) 409 384 return -1; 410 - } 411 385 412 386 /* 413 387 * Workaround for Initiators that have broken connection recovery logic. 414 388 * 415 389 * "We would really like to get rid of this." Linux-iSCSI.org team 416 390 */ 417 - memset(buf, 0, 32); 418 - sprintf(buf, "ErrorRecoveryLevel=%d", na->default_erl); 419 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 420 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 421 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 391 + if (iscsi_change_param_sprintf(conn, "ErrorRecoveryLevel=%d", na->default_erl)) 422 392 return -1; 423 - } 424 393 425 394 if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0) 426 395 return -1; ··· 422 411 unsigned long mrdsl, off; 423 412 int rc; 424 413 425 - sprintf(buf, "RDMAExtensions=Yes"); 426 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 427 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 428 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 414 + if (iscsi_change_param_sprintf(conn, "RDMAExtensions=Yes")) 429 415 return -1; 430 - } 416 + 431 417 /* 432 418 * Make MaxRecvDataSegmentLength PAGE_SIZE aligned for 433 419 * Immediate Data + Unsolicitied Data-OUT if necessary.. ··· 454 446 pr_warn("Aligning ISER MaxRecvDataSegmentLength: %lu down" 455 447 " to PAGE_SIZE\n", mrdsl); 456 448 457 - sprintf(buf, "MaxRecvDataSegmentLength=%lu\n", mrdsl); 458 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 459 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 460 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 449 + if (iscsi_change_param_sprintf(conn, "MaxRecvDataSegmentLength=%lu\n", mrdsl)) 461 450 return -1; 462 - } 463 451 /* 464 452 * ISER currently requires that ImmediateData + Unsolicited 465 453 * Data be disabled when protection / signature MRs are enabled. ··· 465 461 (TARGET_PROT_DOUT_STRIP | TARGET_PROT_DOUT_PASS | 466 462 TARGET_PROT_DOUT_INSERT)) { 467 463 468 - sprintf(buf, "ImmediateData=No"); 469 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 470 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 471 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 464 + if (iscsi_change_param_sprintf(conn, "ImmediateData=No")) 472 465 return -1; 473 - } 474 466 475 - sprintf(buf, "InitialR2T=Yes"); 476 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 477 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 478 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 467 + if (iscsi_change_param_sprintf(conn, "InitialR2T=Yes")) 479 468 return -1; 480 - } 469 + 481 470 pr_debug("Forcing ImmediateData=No + InitialR2T=Yes for" 482 471 " T10-PI enabled ISER session\n"); 483 472 } ··· 615 618 * 616 619 * In our case, we have already located the struct iscsi_tiqn at this point. 617 620 */ 618 - memset(buf, 0, 32); 619 - sprintf(buf, "TargetPortalGroupTag=%hu", sess->tpg->tpgt); 620 - if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { 621 - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, 622 - ISCSI_LOGIN_STATUS_NO_RESOURCES); 621 + if (iscsi_change_param_sprintf(conn, "TargetPortalGroupTag=%hu", sess->tpg->tpgt)) 623 622 return -1; 624 - } 625 623 626 624 return iscsi_login_disable_FIM_keys(conn->param_list, conn); 627 625 }