diff options
Diffstat (limited to 'drivers/acpi/nfit.c')
| -rw-r--r-- | drivers/acpi/nfit.c | 67 | 
1 files changed, 48 insertions, 19 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index f7dab53b352a..aa45d4802707 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -233,11 +233,12 @@ static bool add_spa(struct acpi_nfit_desc *acpi_desc,  		struct nfit_table_prev *prev,  		struct acpi_nfit_system_address *spa)  { +	size_t length = min_t(size_t, sizeof(*spa), spa->header.length);  	struct device *dev = acpi_desc->dev;  	struct nfit_spa *nfit_spa;  	list_for_each_entry(nfit_spa, &prev->spas, list) { -		if (memcmp(nfit_spa->spa, spa, sizeof(*spa)) == 0) { +		if (memcmp(nfit_spa->spa, spa, length) == 0) {  			list_move_tail(&nfit_spa->list, &acpi_desc->spas);  			return true;  		} @@ -259,11 +260,12 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc,  		struct nfit_table_prev *prev,  		struct acpi_nfit_memory_map *memdev)  { +	size_t length = min_t(size_t, sizeof(*memdev), memdev->header.length);  	struct device *dev = acpi_desc->dev;  	struct nfit_memdev *nfit_memdev;  	list_for_each_entry(nfit_memdev, &prev->memdevs, list) -		if (memcmp(nfit_memdev->memdev, memdev, sizeof(*memdev)) == 0) { +		if (memcmp(nfit_memdev->memdev, memdev, length) == 0) {  			list_move_tail(&nfit_memdev->list, &acpi_desc->memdevs);  			return true;  		} @@ -284,11 +286,12 @@ static bool add_dcr(struct acpi_nfit_desc *acpi_desc,  		struct nfit_table_prev *prev,  		struct acpi_nfit_control_region *dcr)  { +	size_t length = min_t(size_t, sizeof(*dcr), dcr->header.length);  	struct device *dev = acpi_desc->dev;  	struct nfit_dcr *nfit_dcr;  	list_for_each_entry(nfit_dcr, &prev->dcrs, list) -		if (memcmp(nfit_dcr->dcr, dcr, sizeof(*dcr)) == 0) { +		if (memcmp(nfit_dcr->dcr, dcr, length) == 0) {  			list_move_tail(&nfit_dcr->list, &acpi_desc->dcrs);  			return true;  		} @@ -308,11 +311,12 @@ static bool add_bdw(struct acpi_nfit_desc *acpi_desc,  		struct nfit_table_prev *prev,  		struct acpi_nfit_data_region *bdw)  { +	size_t length = min_t(size_t, sizeof(*bdw), bdw->header.length);  	struct device *dev = acpi_desc->dev;  	struct nfit_bdw *nfit_bdw;  	list_for_each_entry(nfit_bdw, &prev->bdws, list) -		if (memcmp(nfit_bdw->bdw, bdw, sizeof(*bdw)) == 0) { +		if (memcmp(nfit_bdw->bdw, bdw, length) == 0) {  			list_move_tail(&nfit_bdw->list, &acpi_desc->bdws);  			return true;  		} @@ -332,11 +336,12 @@ static bool add_idt(struct acpi_nfit_desc *acpi_desc,  		struct nfit_table_prev *prev,  		struct acpi_nfit_interleave *idt)  { +	size_t length = min_t(size_t, sizeof(*idt), idt->header.length);  	struct device *dev = acpi_desc->dev;  	struct nfit_idt *nfit_idt;  	list_for_each_entry(nfit_idt, &prev->idts, list) -		if (memcmp(nfit_idt->idt, idt, sizeof(*idt)) == 0) { +		if (memcmp(nfit_idt->idt, idt, length) == 0) {  			list_move_tail(&nfit_idt->list, &acpi_desc->idts);  			return true;  		} @@ -356,11 +361,12 @@ static bool add_flush(struct acpi_nfit_desc *acpi_desc,  		struct nfit_table_prev *prev,  		struct acpi_nfit_flush_address *flush)  { +	size_t length = min_t(size_t, sizeof(*flush), flush->header.length);  	struct device *dev = acpi_desc->dev;  	struct nfit_flush *nfit_flush;  	list_for_each_entry(nfit_flush, &prev->flushes, list) -		if (memcmp(nfit_flush->flush, flush, sizeof(*flush)) == 0) { +		if (memcmp(nfit_flush->flush, flush, length) == 0) {  			list_move_tail(&nfit_flush->list, &acpi_desc->flushes);  			return true;  		} @@ -655,7 +661,7 @@ static ssize_t revision_show(struct device *dev,  	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);  	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); -	return sprintf(buf, "%d\n", acpi_desc->nfit->header.revision); +	return sprintf(buf, "%d\n", acpi_desc->acpi_header.revision);  }  static DEVICE_ATTR_RO(revision); @@ -1652,7 +1658,6 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)  	data = (u8 *) acpi_desc->nfit;  	end = data + sz; -	data += sizeof(struct acpi_table_nfit);  	while (!IS_ERR_OR_NULL(data))  		data = add_table(acpi_desc, &prev, data, end); @@ -1748,13 +1753,29 @@ static int acpi_nfit_add(struct acpi_device *adev)  		return PTR_ERR(acpi_desc);  	} -	acpi_desc->nfit = (struct acpi_table_nfit *) tbl; +	/* +	 * Save the acpi header for later and then skip it, +	 * making nfit point to the first nfit table header. +	 */ +	acpi_desc->acpi_header = *tbl; +	acpi_desc->nfit = (void *) tbl + sizeof(struct acpi_table_nfit); +	sz -= sizeof(struct acpi_table_nfit);  	/* Evaluate _FIT and override with that if present */  	status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);  	if (ACPI_SUCCESS(status) && buf.length > 0) { -		acpi_desc->nfit = (struct acpi_table_nfit *)buf.pointer; -		sz = buf.length; +		union acpi_object *obj; +		/* +		 * Adjust for the acpi_object header of the _FIT +		 */ +		obj = buf.pointer; +		if (obj->type == ACPI_TYPE_BUFFER) { +			acpi_desc->nfit = +				(struct acpi_nfit_header *)obj->buffer.pointer; +			sz = obj->buffer.length; +		} else +			dev_dbg(dev, "%s invalid type %d, ignoring _FIT\n", +				 __func__, (int) obj->type);  	}  	rc = acpi_nfit_init(acpi_desc, sz); @@ -1777,7 +1798,8 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)  {  	struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev);  	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; -	struct acpi_table_nfit *nfit_saved; +	struct acpi_nfit_header *nfit_saved; +	union acpi_object *obj;  	struct device *dev = &adev->dev;  	acpi_status status;  	int ret; @@ -1788,7 +1810,7 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)  	if (!dev->driver) {  		/* dev->driver may be null if we're being removed */  		dev_dbg(dev, "%s: no driver found for dev\n", __func__); -		return; +		goto out_unlock;  	}  	if (!acpi_desc) { @@ -1808,12 +1830,19 @@ static void acpi_nfit_notify(struct acpi_device *adev, u32 event)  	}  	nfit_saved = acpi_desc->nfit; -	acpi_desc->nfit = (struct acpi_table_nfit *)buf.pointer; -	ret = acpi_nfit_init(acpi_desc, buf.length); -	if (!ret) { -		/* Merge failed, restore old nfit, and exit */ -		acpi_desc->nfit = nfit_saved; -		dev_err(dev, "failed to merge updated NFIT\n"); +	obj = buf.pointer; +	if (obj->type == ACPI_TYPE_BUFFER) { +		acpi_desc->nfit = +			(struct acpi_nfit_header *)obj->buffer.pointer; +		ret = acpi_nfit_init(acpi_desc, obj->buffer.length); +		if (ret) { +			/* Merge failed, restore old nfit, and exit */ +			acpi_desc->nfit = nfit_saved; +			dev_err(dev, "failed to merge updated NFIT\n"); +		} +	} else { +		/* Bad _FIT, restore old nfit */ +		dev_err(dev, "Invalid _FIT\n");  	}  	kfree(buf.pointer);  | 
