diff mbox series

acpi: nfit: vmalloc-out-of-bounds Read in acpi_nfit_ctl

Message ID 20241111080429.9861-1-surajsonawane0215@gmail.com (mailing list archive)
State Superseded
Headers show
Series acpi: nfit: vmalloc-out-of-bounds Read in acpi_nfit_ctl | expand

Commit Message

Suraj Sonawane Nov. 11, 2024, 8:04 a.m. UTC
Fix an issue detected by syzbot with KASAN:

BUG: KASAN: vmalloc-out-of-bounds in cmd_to_func drivers/acpi/nfit/
core.c:416 [inline]
BUG: KASAN: vmalloc-out-of-bounds in acpi_nfit_ctl+0x20e8/0x24a0
drivers/acpi/nfit/core.c:459

The issue occurs in `cmd_to_func` when the `call_pkg->nd_reserved2`
array is accessed without verifying that `call_pkg` points to a
buffer that is sized appropriately as a `struct nd_cmd_pkg`. This
could lead to out-of-bounds access and undefined behavior if the
buffer does not have sufficient space.

To address this issue, a check was added in `acpi_nfit_ctl()` to
ensure that `buf` is not `NULL` and `buf_len` is greater than or
equal to `sizeof(struct nd_cmd_pkg)` before casting `buf` to
`struct nd_cmd_pkg *`. This ensures safe access to the members of
`call_pkg`, including the `nd_reserved2` array.

This change preventing out-of-bounds reads.

Reported-by: syzbot+7534f060ebda6b8b51b3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=7534f060ebda6b8b51b3 
Tested-by: syzbot+7534f060ebda6b8b51b3@syzkaller.appspotmail.com
Fixes: 906bd684e4b1 ("Merge tag 'spi-fix-v6.12-rc6'")
Signed-off-by: Suraj Sonawane <surajsonawane0215@gmail.com>
---
 drivers/acpi/nfit/core.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

Comments

kernel test robot Nov. 11, 2024, 12:11 p.m. UTC | #1
Hi Suraj,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.12-rc7 next-20241111]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Suraj-Sonawane/acpi-nfit-vmalloc-out-of-bounds-Read-in-acpi_nfit_ctl/20241111-160546
base:   linus/master
patch link:    https://lore.kernel.org/r/20241111080429.9861-1-surajsonawane0215%40gmail.com
patch subject: [PATCH] acpi: nfit: vmalloc-out-of-bounds Read in acpi_nfit_ctl
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20241111/202411112001.OeKx45GR-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241111/202411112001.OeKx45GR-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411112001.OeKx45GR-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from drivers/acpi/nfit/core.c:6:
   In file included from include/linux/libnvdimm.h:14:
   In file included from include/linux/bio.h:10:
   In file included from include/linux/blk_types.h:10:
   In file included from include/linux/bvec.h:10:
   In file included from include/linux/highmem.h:8:
   In file included from include/linux/cacheflush.h:5:
   In file included from arch/x86/include/asm/cacheflush.h:5:
   In file included from include/linux/mm.h:2213:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> drivers/acpi/nfit/core.c:458:7: warning: variable 'out_obj' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/acpi/nfit/core.c:658:12: note: uninitialized use occurs here
     658 |         ACPI_FREE(out_obj);
         |                   ^~~~~~~
   include/acpi/actypes.h:350:55: note: expanded from macro 'ACPI_FREE'
     350 | #define ACPI_FREE(a)                    acpi_os_free (a)
         |                                                       ^
   drivers/acpi/nfit/core.c:458:3: note: remove the 'if' if its condition is always false
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     459 |                         rc = -EINVAL;
         |                         ~~~~~~~~~~~~~
     460 |                         goto out;
         |                         ~~~~~~~~~
     461 |                 }
         |                 ~
>> drivers/acpi/nfit/core.c:458:7: warning: variable 'out_obj' is used uninitialized whenever '||' condition is true [-Wsometimes-uninitialized]
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                     ^~~~~~~~~~~
   drivers/acpi/nfit/core.c:658:12: note: uninitialized use occurs here
     658 |         ACPI_FREE(out_obj);
         |                   ^~~~~~~
   include/acpi/actypes.h:350:55: note: expanded from macro 'ACPI_FREE'
     350 | #define ACPI_FREE(a)                    acpi_os_free (a)
         |                                                       ^
   drivers/acpi/nfit/core.c:458:7: note: remove the '||' if its condition is always false
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                     ^~~~~~~~~~~~~~
   drivers/acpi/nfit/core.c:442:44: note: initialize the variable 'out_obj' to silence this warning
     442 |         union acpi_object in_obj, in_buf, *out_obj;
         |                                                   ^
         |                                                    = NULL
   6 warnings generated.


vim +458 drivers/acpi/nfit/core.c

   436	
   437	int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
   438			unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
   439	{
   440		struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
   441		struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
   442		union acpi_object in_obj, in_buf, *out_obj;
   443		const struct nd_cmd_desc *desc = NULL;
   444		struct device *dev = acpi_desc->dev;
   445		struct nd_cmd_pkg *call_pkg = NULL;
   446		const char *cmd_name, *dimm_name;
   447		unsigned long cmd_mask, dsm_mask;
   448		u32 offset, fw_status = 0;
   449		acpi_handle handle;
   450		const guid_t *guid;
   451		int func, rc, i;
   452		int family = 0;
   453	
   454		if (cmd_rc)
   455			*cmd_rc = -EINVAL;
   456	
   457		if (cmd == ND_CMD_CALL) {
 > 458			if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
   459				rc = -EINVAL;
   460				goto out;
   461			}
   462			call_pkg = (struct nd_cmd_pkg *)buf;
   463		}
   464		func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
   465		if (func < 0)
   466			return func;
   467	
   468		if (nvdimm) {
   469			struct acpi_device *adev = nfit_mem->adev;
   470	
   471			if (!adev)
   472				return -ENOTTY;
   473	
   474			dimm_name = nvdimm_name(nvdimm);
   475			cmd_name = nvdimm_cmd_name(cmd);
   476			cmd_mask = nvdimm_cmd_mask(nvdimm);
   477			dsm_mask = nfit_mem->dsm_mask;
   478			desc = nd_cmd_dimm_desc(cmd);
   479			guid = to_nfit_uuid(nfit_mem->family);
   480			handle = adev->handle;
   481		} else {
   482			struct acpi_device *adev = to_acpi_dev(acpi_desc);
   483	
   484			cmd_name = nvdimm_bus_cmd_name(cmd);
   485			cmd_mask = nd_desc->cmd_mask;
   486			if (cmd == ND_CMD_CALL && call_pkg->nd_family) {
   487				family = call_pkg->nd_family;
   488				if (family > NVDIMM_BUS_FAMILY_MAX ||
   489				    !test_bit(family, &nd_desc->bus_family_mask))
   490					return -EINVAL;
   491				family = array_index_nospec(family,
   492							    NVDIMM_BUS_FAMILY_MAX + 1);
   493				dsm_mask = acpi_desc->family_dsm_mask[family];
   494				guid = to_nfit_bus_uuid(family);
   495			} else {
   496				dsm_mask = acpi_desc->bus_dsm_mask;
   497				guid = to_nfit_uuid(NFIT_DEV_BUS);
   498			}
   499			desc = nd_cmd_bus_desc(cmd);
   500			handle = adev->handle;
   501			dimm_name = "bus";
   502		}
   503	
   504		if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
   505			return -ENOTTY;
   506	
   507		/*
   508		 * Check for a valid command.  For ND_CMD_CALL, we also have to
   509		 * make sure that the DSM function is supported.
   510		 */
   511		if (cmd == ND_CMD_CALL &&
   512		    (func > NVDIMM_CMD_MAX || !test_bit(func, &dsm_mask)))
   513			return -ENOTTY;
   514		else if (!test_bit(cmd, &cmd_mask))
   515			return -ENOTTY;
   516	
   517		in_obj.type = ACPI_TYPE_PACKAGE;
   518		in_obj.package.count = 1;
   519		in_obj.package.elements = &in_buf;
   520		in_buf.type = ACPI_TYPE_BUFFER;
   521		in_buf.buffer.pointer = buf;
   522		in_buf.buffer.length = 0;
   523	
   524		/* libnvdimm has already validated the input envelope */
   525		for (i = 0; i < desc->in_num; i++)
   526			in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
   527					i, buf);
   528	
   529		if (call_pkg) {
   530			/* skip over package wrapper */
   531			in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
   532			in_buf.buffer.length = call_pkg->nd_size_in;
   533		}
   534	
   535		dev_dbg(dev, "%s cmd: %d: family: %d func: %d input length: %d\n",
   536			dimm_name, cmd, family, func, in_buf.buffer.length);
   537		if (payload_dumpable(nvdimm, func))
   538			print_hex_dump_debug("nvdimm in  ", DUMP_PREFIX_OFFSET, 4, 4,
   539					in_buf.buffer.pointer,
   540					min_t(u32, 256, in_buf.buffer.length), true);
   541	
   542		/* call the BIOS, prefer the named methods over _DSM if available */
   543		if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE
   544				&& test_bit(NFIT_MEM_LSR, &nfit_mem->flags))
   545			out_obj = acpi_label_info(handle);
   546		else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA
   547				&& test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
   548			struct nd_cmd_get_config_data_hdr *p = buf;
   549	
   550			out_obj = acpi_label_read(handle, p->in_offset, p->in_length);
   551		} else if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA
   552				&& test_bit(NFIT_MEM_LSW, &nfit_mem->flags)) {
   553			struct nd_cmd_set_config_hdr *p = buf;
   554	
   555			out_obj = acpi_label_write(handle, p->in_offset, p->in_length,
   556					p->in_buf);
   557		} else {
   558			u8 revid;
   559	
   560			if (nvdimm)
   561				revid = nfit_dsm_revid(nfit_mem->family, func);
   562			else
   563				revid = 1;
   564			out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
   565		}
   566	
   567		if (!out_obj) {
   568			dev_dbg(dev, "%s _DSM failed cmd: %s\n", dimm_name, cmd_name);
   569			return -EINVAL;
   570		}
   571	
   572		if (out_obj->type != ACPI_TYPE_BUFFER) {
   573			dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
   574					dimm_name, cmd_name, out_obj->type);
   575			rc = -EINVAL;
   576			goto out;
   577		}
   578	
   579		dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name,
   580				cmd_name, out_obj->buffer.length);
   581		print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4,
   582				out_obj->buffer.pointer,
   583				min_t(u32, 128, out_obj->buffer.length), true);
   584	
   585		if (call_pkg) {
   586			call_pkg->nd_fw_size = out_obj->buffer.length;
   587			memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
   588				out_obj->buffer.pointer,
   589				min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
   590	
   591			ACPI_FREE(out_obj);
   592			/*
   593			 * Need to support FW function w/o known size in advance.
   594			 * Caller can determine required size based upon nd_fw_size.
   595			 * If we return an error (like elsewhere) then caller wouldn't
   596			 * be able to rely upon data returned to make calculation.
   597			 */
   598			if (cmd_rc)
   599				*cmd_rc = 0;
   600			return 0;
   601		}
   602	
   603		for (i = 0, offset = 0; i < desc->out_num; i++) {
   604			u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, buf,
   605					(u32 *) out_obj->buffer.pointer,
   606					out_obj->buffer.length - offset);
   607	
   608			if (offset + out_size > out_obj->buffer.length) {
   609				dev_dbg(dev, "%s output object underflow cmd: %s field: %d\n",
   610						dimm_name, cmd_name, i);
   611				break;
   612			}
   613	
   614			if (in_buf.buffer.length + offset + out_size > buf_len) {
   615				dev_dbg(dev, "%s output overrun cmd: %s field: %d\n",
   616						dimm_name, cmd_name, i);
   617				rc = -ENXIO;
   618				goto out;
   619			}
   620			memcpy(buf + in_buf.buffer.length + offset,
   621					out_obj->buffer.pointer + offset, out_size);
   622			offset += out_size;
   623		}
   624	
   625		/*
   626		 * Set fw_status for all the commands with a known format to be
   627		 * later interpreted by xlat_status().
   628		 */
   629		if (i >= 1 && ((!nvdimm && cmd >= ND_CMD_ARS_CAP
   630						&& cmd <= ND_CMD_CLEAR_ERROR)
   631					|| (nvdimm && cmd >= ND_CMD_SMART
   632						&& cmd <= ND_CMD_VENDOR)))
   633			fw_status = *(u32 *) out_obj->buffer.pointer;
   634	
   635		if (offset + in_buf.buffer.length < buf_len) {
   636			if (i >= 1) {
   637				/*
   638				 * status valid, return the number of bytes left
   639				 * unfilled in the output buffer
   640				 */
   641				rc = buf_len - offset - in_buf.buffer.length;
   642				if (cmd_rc)
   643					*cmd_rc = xlat_status(nvdimm, buf, cmd,
   644							fw_status);
   645			} else {
   646				dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n",
   647						__func__, dimm_name, cmd_name, buf_len,
   648						offset);
   649				rc = -ENXIO;
   650			}
   651		} else {
   652			rc = 0;
   653			if (cmd_rc)
   654				*cmd_rc = xlat_status(nvdimm, buf, cmd, fw_status);
   655		}
   656	
   657	 out:
   658		ACPI_FREE(out_obj);
   659	
   660		return rc;
   661	}
   662	EXPORT_SYMBOL_GPL(acpi_nfit_ctl);
   663
kernel test robot Nov. 11, 2024, 3:36 p.m. UTC | #2
Hi Suraj,

kernel test robot noticed the following build warnings:

[auto build test WARNING on linus/master]
[also build test WARNING on v6.12-rc7 next-20241111]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Suraj-Sonawane/acpi-nfit-vmalloc-out-of-bounds-Read-in-acpi_nfit_ctl/20241111-160546
base:   linus/master
patch link:    https://lore.kernel.org/r/20241111080429.9861-1-surajsonawane0215%40gmail.com
patch subject: [PATCH] acpi: nfit: vmalloc-out-of-bounds Read in acpi_nfit_ctl
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20241111/202411112349.khM9ZvDJ-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241111/202411112349.khM9ZvDJ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411112349.khM9ZvDJ-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:6:
   In file included from include/linux/libnvdimm.h:14:
   In file included from include/linux/bio.h:10:
   In file included from include/linux/blk_types.h:10:
   In file included from include/linux/bvec.h:10:
   In file included from include/linux/highmem.h:8:
   In file included from include/linux/cacheflush.h:5:
   In file included from arch/x86/include/asm/cacheflush.h:5:
   In file included from include/linux/mm.h:2213:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:458:7: warning: variable 'out_obj' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:658:12: note: uninitialized use occurs here
     658 |         ACPI_FREE(out_obj);
         |                   ^~~~~~~
   include/acpi/actypes.h:350:55: note: expanded from macro 'ACPI_FREE'
     350 | #define ACPI_FREE(a)                    acpi_os_free (a)
         |                                                       ^
   tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:458:3: note: remove the 'if' if its condition is always false
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     459 |                         rc = -EINVAL;
         |                         ~~~~~~~~~~~~~
     460 |                         goto out;
         |                         ~~~~~~~~~
     461 |                 }
         |                 ~
>> tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:458:7: warning: variable 'out_obj' is used uninitialized whenever '||' condition is true [-Wsometimes-uninitialized]
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                     ^~~~~~~~~~~
   tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:658:12: note: uninitialized use occurs here
     658 |         ACPI_FREE(out_obj);
         |                   ^~~~~~~
   include/acpi/actypes.h:350:55: note: expanded from macro 'ACPI_FREE'
     350 | #define ACPI_FREE(a)                    acpi_os_free (a)
         |                                                       ^
   tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:458:7: note: remove the '||' if its condition is always false
     458 |                 if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
         |                     ^~~~~~~~~~~~~~
   tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c:442:44: note: initialize the variable 'out_obj' to silence this warning
     442 |         union acpi_object in_obj, in_buf, *out_obj;
         |                                                   ^
         |                                                    = NULL
   6 warnings generated.


vim +458 tools/testing/nvdimm/../../../drivers/acpi/nfit/core.c

   436	
   437	int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
   438			unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
   439	{
   440		struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
   441		struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
   442		union acpi_object in_obj, in_buf, *out_obj;
   443		const struct nd_cmd_desc *desc = NULL;
   444		struct device *dev = acpi_desc->dev;
   445		struct nd_cmd_pkg *call_pkg = NULL;
   446		const char *cmd_name, *dimm_name;
   447		unsigned long cmd_mask, dsm_mask;
   448		u32 offset, fw_status = 0;
   449		acpi_handle handle;
   450		const guid_t *guid;
   451		int func, rc, i;
   452		int family = 0;
   453	
   454		if (cmd_rc)
   455			*cmd_rc = -EINVAL;
   456	
   457		if (cmd == ND_CMD_CALL) {
 > 458			if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
   459				rc = -EINVAL;
   460				goto out;
   461			}
   462			call_pkg = (struct nd_cmd_pkg *)buf;
   463		}
   464		func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
   465		if (func < 0)
   466			return func;
   467	
   468		if (nvdimm) {
   469			struct acpi_device *adev = nfit_mem->adev;
   470	
   471			if (!adev)
   472				return -ENOTTY;
   473	
   474			dimm_name = nvdimm_name(nvdimm);
   475			cmd_name = nvdimm_cmd_name(cmd);
   476			cmd_mask = nvdimm_cmd_mask(nvdimm);
   477			dsm_mask = nfit_mem->dsm_mask;
   478			desc = nd_cmd_dimm_desc(cmd);
   479			guid = to_nfit_uuid(nfit_mem->family);
   480			handle = adev->handle;
   481		} else {
   482			struct acpi_device *adev = to_acpi_dev(acpi_desc);
   483	
   484			cmd_name = nvdimm_bus_cmd_name(cmd);
   485			cmd_mask = nd_desc->cmd_mask;
   486			if (cmd == ND_CMD_CALL && call_pkg->nd_family) {
   487				family = call_pkg->nd_family;
   488				if (family > NVDIMM_BUS_FAMILY_MAX ||
   489				    !test_bit(family, &nd_desc->bus_family_mask))
   490					return -EINVAL;
   491				family = array_index_nospec(family,
   492							    NVDIMM_BUS_FAMILY_MAX + 1);
   493				dsm_mask = acpi_desc->family_dsm_mask[family];
   494				guid = to_nfit_bus_uuid(family);
   495			} else {
   496				dsm_mask = acpi_desc->bus_dsm_mask;
   497				guid = to_nfit_uuid(NFIT_DEV_BUS);
   498			}
   499			desc = nd_cmd_bus_desc(cmd);
   500			handle = adev->handle;
   501			dimm_name = "bus";
   502		}
   503	
   504		if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
   505			return -ENOTTY;
   506	
   507		/*
   508		 * Check for a valid command.  For ND_CMD_CALL, we also have to
   509		 * make sure that the DSM function is supported.
   510		 */
   511		if (cmd == ND_CMD_CALL &&
   512		    (func > NVDIMM_CMD_MAX || !test_bit(func, &dsm_mask)))
   513			return -ENOTTY;
   514		else if (!test_bit(cmd, &cmd_mask))
   515			return -ENOTTY;
   516	
   517		in_obj.type = ACPI_TYPE_PACKAGE;
   518		in_obj.package.count = 1;
   519		in_obj.package.elements = &in_buf;
   520		in_buf.type = ACPI_TYPE_BUFFER;
   521		in_buf.buffer.pointer = buf;
   522		in_buf.buffer.length = 0;
   523	
   524		/* libnvdimm has already validated the input envelope */
   525		for (i = 0; i < desc->in_num; i++)
   526			in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
   527					i, buf);
   528	
   529		if (call_pkg) {
   530			/* skip over package wrapper */
   531			in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
   532			in_buf.buffer.length = call_pkg->nd_size_in;
   533		}
   534	
   535		dev_dbg(dev, "%s cmd: %d: family: %d func: %d input length: %d\n",
   536			dimm_name, cmd, family, func, in_buf.buffer.length);
   537		if (payload_dumpable(nvdimm, func))
   538			print_hex_dump_debug("nvdimm in  ", DUMP_PREFIX_OFFSET, 4, 4,
   539					in_buf.buffer.pointer,
   540					min_t(u32, 256, in_buf.buffer.length), true);
   541	
   542		/* call the BIOS, prefer the named methods over _DSM if available */
   543		if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE
   544				&& test_bit(NFIT_MEM_LSR, &nfit_mem->flags))
   545			out_obj = acpi_label_info(handle);
   546		else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA
   547				&& test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
   548			struct nd_cmd_get_config_data_hdr *p = buf;
   549	
   550			out_obj = acpi_label_read(handle, p->in_offset, p->in_length);
   551		} else if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA
   552				&& test_bit(NFIT_MEM_LSW, &nfit_mem->flags)) {
   553			struct nd_cmd_set_config_hdr *p = buf;
   554	
   555			out_obj = acpi_label_write(handle, p->in_offset, p->in_length,
   556					p->in_buf);
   557		} else {
   558			u8 revid;
   559	
   560			if (nvdimm)
   561				revid = nfit_dsm_revid(nfit_mem->family, func);
   562			else
   563				revid = 1;
   564			out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
   565		}
   566	
   567		if (!out_obj) {
   568			dev_dbg(dev, "%s _DSM failed cmd: %s\n", dimm_name, cmd_name);
   569			return -EINVAL;
   570		}
   571	
   572		if (out_obj->type != ACPI_TYPE_BUFFER) {
   573			dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
   574					dimm_name, cmd_name, out_obj->type);
   575			rc = -EINVAL;
   576			goto out;
   577		}
   578	
   579		dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name,
   580				cmd_name, out_obj->buffer.length);
   581		print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4,
   582				out_obj->buffer.pointer,
   583				min_t(u32, 128, out_obj->buffer.length), true);
   584	
   585		if (call_pkg) {
   586			call_pkg->nd_fw_size = out_obj->buffer.length;
   587			memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
   588				out_obj->buffer.pointer,
   589				min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
   590	
   591			ACPI_FREE(out_obj);
   592			/*
   593			 * Need to support FW function w/o known size in advance.
   594			 * Caller can determine required size based upon nd_fw_size.
   595			 * If we return an error (like elsewhere) then caller wouldn't
   596			 * be able to rely upon data returned to make calculation.
   597			 */
   598			if (cmd_rc)
   599				*cmd_rc = 0;
   600			return 0;
   601		}
   602	
   603		for (i = 0, offset = 0; i < desc->out_num; i++) {
   604			u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, buf,
   605					(u32 *) out_obj->buffer.pointer,
   606					out_obj->buffer.length - offset);
   607	
   608			if (offset + out_size > out_obj->buffer.length) {
   609				dev_dbg(dev, "%s output object underflow cmd: %s field: %d\n",
   610						dimm_name, cmd_name, i);
   611				break;
   612			}
   613	
   614			if (in_buf.buffer.length + offset + out_size > buf_len) {
   615				dev_dbg(dev, "%s output overrun cmd: %s field: %d\n",
   616						dimm_name, cmd_name, i);
   617				rc = -ENXIO;
   618				goto out;
   619			}
   620			memcpy(buf + in_buf.buffer.length + offset,
   621					out_obj->buffer.pointer + offset, out_size);
   622			offset += out_size;
   623		}
   624	
   625		/*
   626		 * Set fw_status for all the commands with a known format to be
   627		 * later interpreted by xlat_status().
   628		 */
   629		if (i >= 1 && ((!nvdimm && cmd >= ND_CMD_ARS_CAP
   630						&& cmd <= ND_CMD_CLEAR_ERROR)
   631					|| (nvdimm && cmd >= ND_CMD_SMART
   632						&& cmd <= ND_CMD_VENDOR)))
   633			fw_status = *(u32 *) out_obj->buffer.pointer;
   634	
   635		if (offset + in_buf.buffer.length < buf_len) {
   636			if (i >= 1) {
   637				/*
   638				 * status valid, return the number of bytes left
   639				 * unfilled in the output buffer
   640				 */
   641				rc = buf_len - offset - in_buf.buffer.length;
   642				if (cmd_rc)
   643					*cmd_rc = xlat_status(nvdimm, buf, cmd,
   644							fw_status);
   645			} else {
   646				dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n",
   647						__func__, dimm_name, cmd_name, buf_len,
   648						offset);
   649				rc = -ENXIO;
   650			}
   651		} else {
   652			rc = 0;
   653			if (cmd_rc)
   654				*cmd_rc = xlat_status(nvdimm, buf, cmd, fw_status);
   655		}
   656	
   657	 out:
   658		ACPI_FREE(out_obj);
   659	
   660		return rc;
   661	}
   662	EXPORT_SYMBOL_GPL(acpi_nfit_ctl);
   663
diff mbox series

Patch

diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 5429ec9ef..4a2997b60 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -454,8 +454,13 @@  int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
 	if (cmd_rc)
 		*cmd_rc = -EINVAL;
 
-	if (cmd == ND_CMD_CALL)
-		call_pkg = buf;
+	if (cmd == ND_CMD_CALL) {
+		if (buf == NULL || buf_len < sizeof(struct nd_cmd_pkg)) {
+			rc = -EINVAL;
+			goto out;
+		}
+		call_pkg = (struct nd_cmd_pkg *)buf;
+	}
 	func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
 	if (func < 0)
 		return func;