[Bug Report] NVMe-oF/TCP - Slab OOB Read in `nvmet_ctrl_find_get`

Chaitanya Kulkarni chaitanyak at nvidia.com
Wed Nov 8 02:02:50 PST 2023


On 11/8/23 01:01, Alon Zahavi wrote:
> On Wed, 8 Nov 2023 at 10:46, Chaitanya Kulkarni <chaitanyak at nvidia.com> wrote:
>>
>>> ## Root Cause
>>> As explained above, the root cause for this bug is the fact that there
>>> are no NULL terminators to the strings in the object representing the
>>> `struct nvmf_connect_data`.
>> Can you see if this works for you ? it should at least take care of the
>> subsysnqn and hostnqn being accessed as NULL terminated string.
>>
>> diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
>> index 3935165048e7..569046b6a269 100644
>> --- a/drivers/nvme/target/core.c
>> +++ b/drivers/nvme/target/core.c
>> @@ -1235,13 +1235,19 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const
>> char *subsysnqn,
>>                                          const char *hostnqn, u16 cntlid,
>>                                          struct nvmet_req *req)
>>    {
>> +       char subsysnqn_str[NVMF_NQN_SIZE + 1] = { 0 };
>> +       char hostnqn_str[NVMF_NQN_SIZE + 1] = { 0 };
>>           struct nvmet_ctrl *ctrl = NULL;
>>           struct nvmet_subsys *subsys;
>>
>> -       subsys = nvmet_find_get_subsys(req->port, subsysnqn);
>> +       /* subsysnqn & hostnqn may not be NULL ternimated */
>> +       strncpy(subsysnqn_str, subsysnqn, NVMF_NQN_SIZE);
>> +       strncpy(hostnqn_str, hostnqn, NVMF_NQN_SIZE);
>> +
>> +       subsys = nvmet_find_get_subsys(req->port, subsysnqn_str);
>>           if (!subsys) {
>>                   pr_warn("connect request for invalid subsystem %s!\n",
>> -                       subsysnqn);
>> +                       subsysnqn_str);
>>                   req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(subsysnqn);
>>                   goto out;
>>           }
>> @@ -1249,7 +1255,7 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const char
>> *subsysnqn,
>>           mutex_lock(&subsys->lock);
>>           list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) {
>>                   if (ctrl->cntlid == cntlid) {
>> -                       if (strncmp(hostnqn, ctrl->hostnqn,
>> NVMF_NQN_SIZE)) {
>> +                       if (strncmp(hostnqn_str, ctrl->hostnqn,
>> NVMF_NQN_SIZE)) {
>>                                   pr_warn("hostnqn mismatch.\n");
>>                                   continue;
>>                           }
>> @@ -1263,7 +1269,7 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const char
>> *subsysnqn,
>>
>>           ctrl = NULL; /* ctrl not found */
>>           pr_warn("could not find controller %d for subsys %s / host %s\n",
>> -               cntlid, subsysnqn, hostnqn);
>> +               cntlid, subsysnqn_str, hostnqn_str);
>>           req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);
>>
>>    found:
>>
>>
>> -ck
>>
>>
> Before I check it I should say that the same bug occurs in other
> functions as well.
> For example in `nvmet_alloc_ctrl()` there is the same print.
>

yes it has same pattern and will need a same fix here is an updated
patch with nvmet_alloc_ctrl() fix :-


diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 3935165048e7..5c1a71d16df7 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -1235,13 +1235,19 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const 
char *subsysnqn,
                                        const char *hostnqn, u16 cntlid,
                                        struct nvmet_req *req)
  {
+       char subsysnqn_str[NVMF_NQN_SIZE + 1] = { 0 };
+       char hostnqn_str[NVMF_NQN_SIZE + 1] = { 0 };
         struct nvmet_ctrl *ctrl = NULL;
         struct nvmet_subsys *subsys;

-       subsys = nvmet_find_get_subsys(req->port, subsysnqn);
+       /* subsysnqn & hostnqn may not be NULL ternimated */
+       strncpy(subsysnqn_str, subsysnqn, NVMF_NQN_SIZE);
+       strncpy(hostnqn_str, hostnqn, NVMF_NQN_SIZE);
+
+       subsys = nvmet_find_get_subsys(req->port, subsysnqn_str);
         if (!subsys) {
                 pr_warn("connect request for invalid subsystem %s!\n",
-                       subsysnqn);
+                       subsysnqn_str);
                 req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(subsysnqn);
                 goto out;
         }
@@ -1249,7 +1255,7 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const char 
*subsysnqn,
         mutex_lock(&subsys->lock);
         list_for_each_entry(ctrl, &subsys->ctrls, subsys_entry) {
                 if (ctrl->cntlid == cntlid) {
-                       if (strncmp(hostnqn, ctrl->hostnqn, 
NVMF_NQN_SIZE)) {
+                       if (strncmp(hostnqn_str, ctrl->hostnqn, 
NVMF_NQN_SIZE)) {
                                 pr_warn("hostnqn mismatch.\n");
                                 continue;
                         }
@@ -1263,7 +1269,7 @@ struct nvmet_ctrl *nvmet_ctrl_find_get(const char 
*subsysnqn,

         ctrl = NULL; /* ctrl not found */
         pr_warn("could not find controller %d for subsys %s / host %s\n",
-               cntlid, subsysnqn, hostnqn);
+               cntlid, subsysnqn_str, hostnqn_str);
         req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(cntlid);

  found:
@@ -1358,25 +1364,32 @@ static void nvmet_fatal_error_handler(struct 
work_struct *work)
  u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn,
                 struct nvmet_req *req, u32 kato, struct nvmet_ctrl **ctrlp)
  {
+       char subsysnqn_str[NVMF_NQN_SIZE + 1] = { 0 };
+       char hostnqn_str[NVMF_NQN_SIZE + 1] = { 0 };
         struct nvmet_subsys *subsys;
         struct nvmet_ctrl *ctrl;
         int ret;
         u16 status;

         status = NVME_SC_CONNECT_INVALID_PARAM | NVME_SC_DNR;
-       subsys = nvmet_find_get_subsys(req->port, subsysnqn);
+
+       /* subsysnqn & hostnqn may not be NULL ternimated */
+       strncpy(subsysnqn_str, subsysnqn, NVMF_NQN_SIZE);
+       strncpy(hostnqn_str, hostnqn, NVMF_NQN_SIZE);
+
+       subsys = nvmet_find_get_subsys(req->port, subsysnqn_str);
         if (!subsys) {
                 pr_warn("connect request for invalid subsystem %s!\n",
-                       subsysnqn);
+                       subsysnqn_str);
                 req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(subsysnqn);
                 req->error_loc = offsetof(struct nvme_common_command, 
dptr);
                 goto out;
         }

         down_read(&nvmet_config_sem);
-       if (!nvmet_host_allowed(subsys, hostnqn)) {
+       if (!nvmet_host_allowed(subsys, hostnqn_str)) {
                 pr_info("connect by host %s for subsystem %s not 
allowed\n",
-                       hostnqn, subsysnqn);
+                       hostnqn_str, subsysnqn_str);
                 req->cqe->result.u32 = IPO_IATTR_CONNECT_DATA(hostnqn);
                 up_read(&nvmet_config_sem);
                 status = NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR;

-ck




More information about the Linux-nvme mailing list