Char/Misc fixes for 4.15-rc3

Here are some small misc driver fixes for 4.15-rc3 to resolve reported
 issues.  Specifically these are:
 	- binder fix for a memory leak
 	- vpd driver fixes for a number of reported problems
 	- hyperv driver fix for memory accesses where it shouldn't be.
 
 All of these have been in linux-next for a while.  There's also one more
 MAINTAINERS file update that came in today to get the Android
 developer's emails correct, which is also in this pull request, that was
 not in linux-next, but should not be an issue.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWia6IQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ykaKwCgx1lw4qMmetUzjKBX+OMgEkFZo3UAoIL/DLiB
 ZyMb52XWdP0pScSsFUyn
 =riSs
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc fixes from Greg KH:
 "Here are some small misc driver fixes for 4.15-rc3 to resolve reported
  issues. Specifically these are:

   - binder fix for a memory leak

   - vpd driver fixes for a number of reported problems

   - hyperv driver fix for memory accesses where it shouldn't be.

  All of these have been in linux-next for a while. There's also one
  more MAINTAINERS file update that came in today to get the Android
  developer's emails correct, which is also in this pull request, that
  was not in linux-next, but should not be an issue"

* tag 'char-misc-4.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  MAINTAINERS: update Android driver maintainers.
  firmware: vpd: Fix platform driver and device registration/unregistration
  firmware: vpd: Tie firmware kobject to device lifetime
  firmware: vpd: Destroy vpd sections in remove function
  hv: kvp: Avoid reading past allocated blocks from KVP file
  Drivers: hv: vmbus: Fix a rescind issue
  ANDROID: binder: fix transaction leak.
This commit is contained in:
Linus Torvalds 2017-12-05 10:06:23 -08:00
commit 6a5e05a47b
7 changed files with 95 additions and 84 deletions

View File

@ -859,7 +859,8 @@ F: kernel/configs/android*
ANDROID DRIVERS
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
M: Arve Hjønnevåg <arve@android.com>
M: Riley Andrews <riandrews@android.com>
M: Todd Kjos <tkjos@android.com>
M: Martijn Coenen <maco@android.com>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
L: devel@driverdev.osuosl.org
S: Supported

View File

@ -1947,6 +1947,26 @@ static void binder_send_failed_reply(struct binder_transaction *t,
}
}
/**
* binder_cleanup_transaction() - cleans up undelivered transaction
* @t: transaction that needs to be cleaned up
* @reason: reason the transaction wasn't delivered
* @error_code: error to return to caller (if synchronous call)
*/
static void binder_cleanup_transaction(struct binder_transaction *t,
const char *reason,
uint32_t error_code)
{
if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) {
binder_send_failed_reply(t, error_code);
} else {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered transaction %d, %s\n",
t->debug_id, reason);
binder_free_transaction(t);
}
}
/**
* binder_validate_object() - checks for a valid metadata object in a buffer.
* @buffer: binder_buffer that we're parsing.
@ -4015,12 +4035,20 @@ retry:
if (put_user(cmd, (uint32_t __user *)ptr)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "put_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr))) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "copy_to_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
ptr += sizeof(tr);
@ -4090,15 +4118,9 @@ static void binder_release_work(struct binder_proc *proc,
struct binder_transaction *t;
t = container_of(w, struct binder_transaction, work);
if (t->buffer->target_node &&
!(t->flags & TF_ONE_WAY)) {
binder_send_failed_reply(t, BR_DEAD_REPLY);
} else {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered transaction %d\n",
t->debug_id);
binder_free_transaction(t);
}
binder_cleanup_transaction(t, "process died.",
BR_DEAD_REPLY);
} break;
case BINDER_WORK_RETURN_ERROR: {
struct binder_error *e = container_of(

View File

@ -295,38 +295,60 @@ static int vpd_probe(struct platform_device *pdev)
if (ret)
return ret;
return vpd_sections_init(entry.cbmem_addr);
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
if (!vpd_kobj)
return -ENOMEM;
ret = vpd_sections_init(entry.cbmem_addr);
if (ret) {
kobject_put(vpd_kobj);
return ret;
}
return 0;
}
static int vpd_remove(struct platform_device *pdev)
{
vpd_section_destroy(&ro_vpd);
vpd_section_destroy(&rw_vpd);
kobject_put(vpd_kobj);
return 0;
}
static struct platform_driver vpd_driver = {
.probe = vpd_probe,
.remove = vpd_remove,
.driver = {
.name = "vpd",
},
};
static struct platform_device *vpd_pdev;
static int __init vpd_platform_init(void)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_register_simple("vpd", -1, NULL, 0);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ret = platform_driver_register(&vpd_driver);
if (ret)
return ret;
vpd_kobj = kobject_create_and_add("vpd", firmware_kobj);
if (!vpd_kobj)
return -ENOMEM;
platform_driver_register(&vpd_driver);
vpd_pdev = platform_device_register_simple("vpd", -1, NULL, 0);
if (IS_ERR(vpd_pdev)) {
platform_driver_unregister(&vpd_driver);
return PTR_ERR(vpd_pdev);
}
return 0;
}
static void __exit vpd_platform_exit(void)
{
vpd_section_destroy(&ro_vpd);
vpd_section_destroy(&rw_vpd);
kobject_put(vpd_kobj);
platform_device_unregister(vpd_pdev);
platform_driver_unregister(&vpd_driver);
}
module_init(vpd_platform_init);

View File

@ -659,22 +659,28 @@ void vmbus_close(struct vmbus_channel *channel)
*/
return;
}
mutex_lock(&vmbus_connection.channel_mutex);
/*
* Close all the sub-channels first and then close the
* primary channel.
*/
list_for_each_safe(cur, tmp, &channel->sc_list) {
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
vmbus_close_internal(cur_channel);
if (cur_channel->rescind) {
wait_for_completion(&cur_channel->rescind_event);
mutex_lock(&vmbus_connection.channel_mutex);
vmbus_close_internal(cur_channel);
hv_process_channel_removal(
cur_channel->offermsg.child_relid);
} else {
mutex_lock(&vmbus_connection.channel_mutex);
vmbus_close_internal(cur_channel);
}
mutex_unlock(&vmbus_connection.channel_mutex);
}
/*
* Now close the primary.
*/
mutex_lock(&vmbus_connection.channel_mutex);
vmbus_close_internal(channel);
mutex_unlock(&vmbus_connection.channel_mutex);
}

View File

@ -333,6 +333,7 @@ static struct vmbus_channel *alloc_channel(void)
return NULL;
spin_lock_init(&channel->lock);
init_completion(&channel->rescind_event);
INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list);
@ -898,6 +899,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
/*
* Now wait for offer handling to complete.
*/
vmbus_rescind_cleanup(channel);
while (READ_ONCE(channel->probe_done) == false) {
/*
* We wait here until any channel offer is currently
@ -913,7 +915,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
if (channel->device_obj) {
if (channel->chn_rescind_callback) {
channel->chn_rescind_callback(channel);
vmbus_rescind_cleanup(channel);
return;
}
/*
@ -922,7 +923,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
*/
dev = get_device(&channel->device_obj->device);
if (dev) {
vmbus_rescind_cleanup(channel);
vmbus_device_unregister(channel->device_obj);
put_device(dev);
}
@ -936,13 +936,14 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
* 2. Then close the primary channel.
*/
mutex_lock(&vmbus_connection.channel_mutex);
vmbus_rescind_cleanup(channel);
if (channel->state == CHANNEL_OPEN_STATE) {
/*
* The channel is currently not open;
* it is safe for us to cleanup the channel.
*/
hv_process_channel_removal(rescind->child_relid);
} else {
complete(&channel->rescind_event);
}
mutex_unlock(&vmbus_connection.channel_mutex);
}

View File

@ -708,6 +708,7 @@ struct vmbus_channel {
u8 monitor_bit;
bool rescind; /* got rescind msg */
struct completion rescind_event;
u32 ringbuffer_gpadlhandle;

View File

@ -193,11 +193,14 @@ static void kvp_update_mem_state(int pool)
for (;;) {
readp = &record[records_read];
records_read += fread(readp, sizeof(struct kvp_record),
ENTRIES_PER_BLOCK * num_blocks,
filep);
ENTRIES_PER_BLOCK * num_blocks - records_read,
filep);
if (ferror(filep)) {
syslog(LOG_ERR, "Failed to read file, pool: %d", pool);
syslog(LOG_ERR,
"Failed to read file, pool: %d; error: %d %s",
pool, errno, strerror(errno));
kvp_release_lock(pool);
exit(EXIT_FAILURE);
}
@ -210,6 +213,7 @@ static void kvp_update_mem_state(int pool)
if (record == NULL) {
syslog(LOG_ERR, "malloc failed");
kvp_release_lock(pool);
exit(EXIT_FAILURE);
}
continue;
@ -224,15 +228,11 @@ static void kvp_update_mem_state(int pool)
fclose(filep);
kvp_release_lock(pool);
}
static int kvp_file_init(void)
{
int fd;
FILE *filep;
size_t records_read;
char *fname;
struct kvp_record *record;
struct kvp_record *readp;
int num_blocks;
int i;
int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK;
@ -246,61 +246,19 @@ static int kvp_file_init(void)
for (i = 0; i < KVP_POOL_COUNT; i++) {
fname = kvp_file_info[i].fname;
records_read = 0;
num_blocks = 1;
sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i);
fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */);
if (fd == -1)
return 1;
filep = fopen(fname, "re");
if (!filep) {
close(fd);
return 1;
}
record = malloc(alloc_unit * num_blocks);
if (record == NULL) {
fclose(filep);
close(fd);
return 1;
}
for (;;) {
readp = &record[records_read];
records_read += fread(readp, sizeof(struct kvp_record),
ENTRIES_PER_BLOCK,
filep);
if (ferror(filep)) {
syslog(LOG_ERR, "Failed to read file, pool: %d",
i);
exit(EXIT_FAILURE);
}
if (!feof(filep)) {
/*
* We have more data to read.
*/
num_blocks++;
record = realloc(record, alloc_unit *
num_blocks);
if (record == NULL) {
fclose(filep);
close(fd);
return 1;
}
continue;
}
break;
}
kvp_file_info[i].fd = fd;
kvp_file_info[i].num_blocks = num_blocks;
kvp_file_info[i].records = record;
kvp_file_info[i].num_records = records_read;
fclose(filep);
kvp_file_info[i].num_blocks = 1;
kvp_file_info[i].records = malloc(alloc_unit);
if (kvp_file_info[i].records == NULL)
return 1;
kvp_file_info[i].num_records = 0;
kvp_update_mem_state(i);
}
return 0;