The function nvmet_subsys_attr_qid_max_store() can race against nvmet_alloc_ctrl() when a subsystem's max_qid limit is modified. Suppose max_qid is currently 64. If nvmet_alloc_ctrl() executes: ctrl->sqs = kzalloc_objs(struct nvmet_sq *, subsys->max_qid + 1); and at this exact point, a userspace process changes max_qid to 128, nvmet_subsys_attr_qid_max_store() will set the new max_qid value. It attempts to delete active controllers to force a reconnect, but the new controller won't be deleted because it hasn't been added to the subsys->ctrls list yet. nvmet_alloc_ctrl() then proceeds and adds the new controller to the subsys->ctrls list. Later, when nvmet_install_queue() is called, it will see max_qid set to 128, but the memory allocated for sqs is only sized for 64 entries. This results in a KASAN out-of-bounds warning and potential memory corruptions. Fix this by protecting the queue allocations and list insertion in nvmet_alloc_ctrl() with down_read(&nvmet_config_sem). Because nvmet_subsys_attr_qid_max_store() acquires down_write(&nvmet_config_sem) to modify the attribute, this safely prevents the configfs writer from modifying max_qid during controller creation. Copy the max_qid from the subsystem to the controller's structure during the allocation; ctrl->max_qid never changes as long as the controller remains in LIVE state, so this will prevent similar race conditions.
Unified · Split
| @@ -1337,7 +1337,7 @@ static u16 nvmet_set_feat_arbitration(struct nvmet_req *req) · 1336 unmodified lines | ||
| 1337 | 1337 | |
| 1338 | 1338 | void nvmet_execute_set_features(struct nvmet_req *req) |
| 1339 | 1339 | { |
| 1340 | − struct nvmet_subsys *subsys = nvmet_req_subsys(req); | |
| 1340 | + struct nvmet_ctrl *ctrl = nvmet_req_ctrl(req); | |
| 1341 | 1341 | u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10); |
| 1342 | 1342 | u32 cdw11 = le32_to_cpu(req->cmd->common.cdw11); |
| 1343 | 1343 | u16 status = 0; |
| @@ -1359,7 +1359,7 @@ void nvmet_execute_set_features(struct nvmet_req *req) · 15 unmodified lines | ||
| 1359 | 1359 | break; |
| 1360 | 1360 | } |
| 1361 | 1361 | nvmet_set_result(req, |
| 1362 | − (subsys->max_qid - 1) | ((subsys->max_qid - 1) << 16)); | |
| 1362 | + (ctrl->max_qid - 1) | ((ctrl->max_qid - 1) << 16)); | |
| 1363 | 1363 | break; |
| 1364 | 1364 | case NVME_FEAT_IRQ_COALESCE: |
| 1365 | 1365 | status = nvmet_set_feat_irq_coalesce(req); |
| @@ -1496,7 +1496,7 @@ void nvmet_get_feat_async_event(struct nvmet_req *req) · 130 unmodified lines | ||
| 1496 | 1496 | |
| 1497 | 1497 | void nvmet_execute_get_features(struct nvmet_req *req) |
| 1498 | 1498 | { |
| 1499 | − struct nvmet_subsys *subsys = nvmet_req_subsys(req); | |
| 1499 | + struct nvmet_ctrl *ctrl = nvmet_req_ctrl(req); | |
| 1500 | 1500 | u32 cdw10 = le32_to_cpu(req->cmd->common.cdw10); |
| 1501 | 1501 | u16 status = 0; |
| 1502 | 1502 | |
| @@ -1536,7 +1536,7 @@ void nvmet_execute_get_features(struct nvmet_req *req) · 33 unmodified lines | ||
| 1536 | 1536 | break; |
| 1537 | 1537 | case NVME_FEAT_NUM_QUEUES: |
| 1538 | 1538 | nvmet_set_result(req, |
| 1539 | − (subsys->max_qid-1) | ((subsys->max_qid-1) << 16)); | |
| 1539 | + (ctrl->max_qid-1) | ((ctrl->max_qid-1) << 16)); | |
| 1540 | 1540 | break; |
| 1541 | 1541 | case NVME_FEAT_KATO: |
| 1542 | 1542 | nvmet_get_feat_kato(req); |
| @@ -878,7 +878,7 @@ u16 nvmet_check_cqid(struct nvmet_ctrl *ctrl, u16 cqid, bool create) · 877 unmodified lines | ||
| 878 | 878 | if (!ctrl->cqs) |
| 879 | 879 | return NVME_SC_INTERNAL | NVME_STATUS_DNR; |
| 880 | 880 | |
| 881 | − if (cqid > ctrl->subsys->max_qid) | |
| 881 | + if (cqid > ctrl->max_qid) | |
| 882 | 882 | return NVME_SC_QID_INVALID | NVME_STATUS_DNR; |
| 883 | 883 | |
| 884 | 884 | if ((create && ctrl->cqs[cqid]) || (!create && !ctrl->cqs[cqid])) |
| @@ -926,7 +926,7 @@ u16 nvmet_check_sqid(struct nvmet_ctrl *ctrl, u16 sqid, · 41 unmodified lines | ||
| 926 | 926 | if (!ctrl->sqs) |
| 927 | 927 | return NVME_SC_INTERNAL | NVME_STATUS_DNR; |
| 928 | 928 | |
| 929 | − if (sqid > ctrl->subsys->max_qid) | |
| 929 | + if (sqid > ctrl->max_qid) | |
| 930 | 930 | return NVME_SC_QID_INVALID | NVME_STATUS_DNR; |
| 931 | 931 | |
| 932 | 932 | if ((create && ctrl->sqs[sqid]) || |
| @@ -1655,11 +1655,29 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) · 722 unmodified lines | ||
| 1655 | 1655 | if (!ctrl->changed_ns_list) |
| 1656 | 1656 | goto out_free_ctrl; |
| 1657 | 1657 | |
| 1658 | − ctrl->sqs = kzalloc_objs(struct nvmet_sq *, subsys->max_qid + 1); | |
| 1658 | + /* | |
| 1659 | + * Discovery controllers may use some arbitrary high value | |
| 1660 | + * in order to cleanup stale discovery sessions | |
| 1661 | + */ | |
| 1662 | + if (nvmet_is_disc_subsys(ctrl->subsys) && !kato) | |
| 1663 | + kato = NVMET_DISC_KATO_MS; | |
| 1664 | + | |
| 1665 | + /* keep-alive timeout in seconds */ | |
| 1666 | + ctrl->kato = DIV_ROUND_UP(kato, 1000); | |
| 1667 | + | |
| 1668 | + ctrl->err_counter = 0; | |
| 1669 | + spin_lock_init(&ctrl->error_lock); | |
| 1670 | + | |
| 1671 | + down_read(&nvmet_config_sem); | |
| 1672 | + mutex_lock(&subsys->lock); | |
| 1673 | + | |
| 1674 | + ctrl->max_qid = subsys->max_qid; | |
| 1675 | + | |
| 1676 | + ctrl->sqs = kzalloc_objs(struct nvmet_sq *, ctrl->max_qid + 1); | |
| 1659 | 1677 | if (!ctrl->sqs) |
| 1660 | 1678 | goto out_free_changed_ns_list; |
| 1661 | 1679 | |
| 1662 | − ctrl->cqs = kzalloc_objs(struct nvmet_cq *, subsys->max_qid + 1); | |
| 1680 | + ctrl->cqs = kzalloc_objs(struct nvmet_cq *, ctrl->max_qid + 1); | |
| 1663 | 1681 | if (!ctrl->cqs) |
| 1664 | 1682 | goto out_free_sqs; |
| 1665 | 1683 | |
| @@ -1672,22 +1690,6 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) · 6 unmodified lines | ||
| 1672 | 1690 | } |
| 1673 | 1691 | ctrl->cntlid = ret; |
| 1674 | 1692 | |
| 1675 | − /* | |
| 1676 | − * Discovery controllers may use some arbitrary high value | |
| 1677 | − * in order to cleanup stale discovery sessions | |
| 1678 | − */ | |
| 1679 | − if (nvmet_is_disc_subsys(ctrl->subsys) && !kato) | |
| 1680 | − kato = NVMET_DISC_KATO_MS; | |
| 1681 | − | |
| 1682 | − /* keep-alive timeout in seconds */ | |
| 1683 | − ctrl->kato = DIV_ROUND_UP(kato, 1000); | |
| 1684 | − | |
| 1685 | − ctrl->err_counter = 0; | |
| 1686 | − spin_lock_init(&ctrl->error_lock); | |
| 1687 | − | |
| 1688 | − nvmet_start_keep_alive_timer(ctrl); | |
| 1689 | − | |
| 1690 | − mutex_lock(&subsys->lock); | |
| 1691 | 1693 | ret = nvmet_ctrl_init_pr(ctrl); |
| 1692 | 1694 | if (ret) |
| 1693 | 1695 | goto init_pr_fail; |
| @@ -1695,6 +1697,9 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) · 1 unmodified line | ||
| 1695 | 1697 | nvmet_setup_p2p_ns_map(ctrl, args->p2p_client); |
| 1696 | 1698 | nvmet_debugfs_ctrl_setup(ctrl); |
| 1697 | 1699 | mutex_unlock(&subsys->lock); |
| 1700 | + up_read(&nvmet_config_sem); | |
| 1701 | + | |
| 1702 | + nvmet_start_keep_alive_timer(ctrl); | |
| 1698 | 1703 | |
| 1699 | 1704 | if (args->hostid) |
| 1700 | 1705 | uuid_copy(&ctrl->hostid, args->hostid); |
| @@ -1724,14 +1729,14 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args) · 23 unmodified lines | ||
| 1724 | 1729 | return ctrl; |
| 1725 | 1730 | |
| 1726 | 1731 | init_pr_fail: |
| 1727 | − mutex_unlock(&subsys->lock); | |
| 1728 | − nvmet_stop_keep_alive_timer(ctrl); | |
| 1729 | 1732 | ida_free(&cntlid_ida, ctrl->cntlid); |
| 1730 | 1733 | out_free_cqs: |
| 1731 | 1734 | kfree(ctrl->cqs); |
| 1732 | 1735 | out_free_sqs: |
| 1733 | 1736 | kfree(ctrl->sqs); |
| 1734 | 1737 | out_free_changed_ns_list: |
| 1738 | + mutex_unlock(&subsys->lock); | |
| 1739 | + up_read(&nvmet_config_sem); | |
| 1735 | 1740 | kfree(ctrl->changed_ns_list); |
| 1736 | 1741 | out_free_ctrl: |
| 1737 | 1742 | kfree(ctrl); |
| @@ -370,7 +370,7 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) · 369 unmodified lines | ||
| 370 | 370 | goto out; |
| 371 | 371 | } |
| 372 | 372 | |
| 373 | − if (unlikely(qid > ctrl->subsys->max_qid)) { | |
| 373 | + if (unlikely(qid > ctrl->max_qid)) { | |
| 374 | 374 | pr_warn("invalid queue id (%d)\n", qid); |
| 375 | 375 | status = NVME_SC_CONNECT_INVALID_PARAM | NVME_STATUS_DNR; |
| 376 | 376 | req->cqe->result.u32 = IPO_IATTR_CONNECT_SQE(qid); |
| @@ -268,6 +268,7 @@ struct nvmet_ctrl { · 267 unmodified lines | ||
| 268 | 268 | |
| 269 | 269 | uuid_t hostid; |
| 270 | 270 | u16 cntlid; |
| 271 | + u16 max_qid; | |
| 271 | 272 | u32 kato; |
| 272 | 273 | |
| 273 | 274 | struct nvmet_port *port; |
| @@ -756,6 +757,11 @@ static inline struct nvmet_subsys *nvmet_req_subsys(struct nvmet_req *req) · 482 unmodified lines | ||
| 756 | 757 | return req->sq->ctrl->subsys; |
| 757 | 758 | } |
| 758 | 759 | |
| 760 | +static inline struct nvmet_ctrl *nvmet_req_ctrl(struct nvmet_req *req) | |
| 761 | +{ | |
| 762 | + return req->sq->ctrl; | |
| 763 | +} | |
| 764 | + | |
| 759 | 765 | static inline bool nvmet_is_disc_subsys(struct nvmet_subsys *subsys) |
| 760 | 766 | { |
| 761 | 767 | return subsys->type != NVME_NQN_NVME; |
| @@ -2081,7 +2081,7 @@ static int nvmet_pci_epf_create_ctrl(struct nvmet_pci_epf *nvme_epf, · 2080 unmodified lines | ||
| 2081 | 2081 | } |
| 2082 | 2082 | |
| 2083 | 2083 | /* Allocate our queues, up to the maximum number. */ |
| 2084 | − ctrl->nr_queues = min(ctrl->tctrl->subsys->max_qid + 1, max_nr_queues); | |
| 2084 | + ctrl->nr_queues = min(ctrl->tctrl->max_qid + 1, max_nr_queues); | |
| 2085 | 2085 | ret = nvmet_pci_epf_alloc_queues(ctrl); |
| 2086 | 2086 | if (ret) |
| 2087 | 2087 | goto out_put_ctrl; |