Char/Misc driver fixes for 4.14-rc5
Here are 4 patches to resolve some char/misc driver issues found these past weeks. One of them is a mei bugfix and another is a new mei device id. There is also a hyper-v fix for a reported issue, and a binder issue fix for a problem reported by a few people. All of these have been in my tree for a while, I don't know if linux-next is really testing much this month. But 0-day is happy with them :) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWeNCTw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ykNOQCgzskhoji+7Fsl+xc3SX/3ntJwoecAnAz5xQIA grpH4IOHq/0aPbQj8g3Y =F3xc -----END PGP SIGNATURE----- Merge tag 'char-misc-4.14-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver fixes from Greg KH: "Here are 4 patches to resolve some char/misc driver issues found these past weeks. One of them is a mei bugfix and another is a new mei device id. There is also a hyper-v fix for a reported issue, and a binder issue fix for a problem reported by a few people. All of these have been in my tree for a while, I don't know if linux-next is really testing much this month. But 0-day is happy with them :)" * tag 'char-misc-4.14-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: binder: fix use-after-free in binder_transaction() Drivers: hv: vmbus: Fix bugs in rescind handling mei: me: add gemini lake devices id mei: always use domain runtime pm callbacks.
This commit is contained in:
commit
ae7df8f985
|
@ -2582,6 +2582,48 @@ static bool binder_proc_transaction(struct binder_transaction *t,
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* binder_get_node_refs_for_txn() - Get required refs on node for txn
|
||||
* @node: struct binder_node for which to get refs
|
||||
* @proc: returns @node->proc if valid
|
||||
* @error: if no @proc then returns BR_DEAD_REPLY
|
||||
*
|
||||
* User-space normally keeps the node alive when creating a transaction
|
||||
* since it has a reference to the target. The local strong ref keeps it
|
||||
* alive if the sending process dies before the target process processes
|
||||
* the transaction. If the source process is malicious or has a reference
|
||||
* counting bug, relying on the local strong ref can fail.
|
||||
*
|
||||
* Since user-space can cause the local strong ref to go away, we also take
|
||||
* a tmpref on the node to ensure it survives while we are constructing
|
||||
* the transaction. We also need a tmpref on the proc while we are
|
||||
* constructing the transaction, so we take that here as well.
|
||||
*
|
||||
* Return: The target_node with refs taken or NULL if no @node->proc is NULL.
|
||||
* Also sets @proc if valid. If the @node->proc is NULL indicating that the
|
||||
* target proc has died, @error is set to BR_DEAD_REPLY
|
||||
*/
|
||||
static struct binder_node *binder_get_node_refs_for_txn(
|
||||
struct binder_node *node,
|
||||
struct binder_proc **procp,
|
||||
uint32_t *error)
|
||||
{
|
||||
struct binder_node *target_node = NULL;
|
||||
|
||||
binder_node_inner_lock(node);
|
||||
if (node->proc) {
|
||||
target_node = node;
|
||||
binder_inc_node_nilocked(node, 1, 0, NULL);
|
||||
binder_inc_node_tmpref_ilocked(node);
|
||||
node->proc->tmp_ref++;
|
||||
*procp = node->proc;
|
||||
} else
|
||||
*error = BR_DEAD_REPLY;
|
||||
binder_node_inner_unlock(node);
|
||||
|
||||
return target_node;
|
||||
}
|
||||
|
||||
static void binder_transaction(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
struct binder_transaction_data *tr, int reply,
|
||||
|
@ -2685,43 +2727,35 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
ref = binder_get_ref_olocked(proc, tr->target.handle,
|
||||
true);
|
||||
if (ref) {
|
||||
binder_inc_node(ref->node, 1, 0, NULL);
|
||||
target_node = ref->node;
|
||||
target_node = binder_get_node_refs_for_txn(
|
||||
ref->node, &target_proc,
|
||||
&return_error);
|
||||
} else {
|
||||
binder_user_error("%d:%d got transaction to invalid handle\n",
|
||||
proc->pid, thread->pid);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
}
|
||||
binder_proc_unlock(proc);
|
||||
if (target_node == NULL) {
|
||||
binder_user_error("%d:%d got transaction to invalid handle\n",
|
||||
proc->pid, thread->pid);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
return_error_param = -EINVAL;
|
||||
return_error_line = __LINE__;
|
||||
goto err_invalid_target_handle;
|
||||
}
|
||||
} else {
|
||||
mutex_lock(&context->context_mgr_node_lock);
|
||||
target_node = context->binder_context_mgr_node;
|
||||
if (target_node == NULL) {
|
||||
if (target_node)
|
||||
target_node = binder_get_node_refs_for_txn(
|
||||
target_node, &target_proc,
|
||||
&return_error);
|
||||
else
|
||||
return_error = BR_DEAD_REPLY;
|
||||
mutex_unlock(&context->context_mgr_node_lock);
|
||||
return_error_line = __LINE__;
|
||||
goto err_no_context_mgr_node;
|
||||
}
|
||||
binder_inc_node(target_node, 1, 0, NULL);
|
||||
mutex_unlock(&context->context_mgr_node_lock);
|
||||
}
|
||||
e->to_node = target_node->debug_id;
|
||||
binder_node_lock(target_node);
|
||||
target_proc = target_node->proc;
|
||||
if (target_proc == NULL) {
|
||||
binder_node_unlock(target_node);
|
||||
return_error = BR_DEAD_REPLY;
|
||||
if (!target_node) {
|
||||
/*
|
||||
* return_error is set above
|
||||
*/
|
||||
return_error_param = -EINVAL;
|
||||
return_error_line = __LINE__;
|
||||
goto err_dead_binder;
|
||||
}
|
||||
binder_inner_proc_lock(target_proc);
|
||||
target_proc->tmp_ref++;
|
||||
binder_inner_proc_unlock(target_proc);
|
||||
binder_node_unlock(target_node);
|
||||
e->to_node = target_node->debug_id;
|
||||
if (security_binder_transaction(proc->tsk,
|
||||
target_proc->tsk) < 0) {
|
||||
return_error = BR_FAILED_REPLY;
|
||||
|
@ -3071,6 +3105,8 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
if (target_thread)
|
||||
binder_thread_dec_tmpref(target_thread);
|
||||
binder_proc_dec_tmpref(target_proc);
|
||||
if (target_node)
|
||||
binder_dec_node_tmpref(target_node);
|
||||
/*
|
||||
* write barrier to synchronize with initialization
|
||||
* of log entry
|
||||
|
@ -3090,6 +3126,8 @@ err_bad_parent:
|
|||
err_copy_data_failed:
|
||||
trace_binder_transaction_failed_buffer_release(t->buffer);
|
||||
binder_transaction_buffer_release(target_proc, t->buffer, offp);
|
||||
if (target_node)
|
||||
binder_dec_node_tmpref(target_node);
|
||||
target_node = NULL;
|
||||
t->buffer->transaction = NULL;
|
||||
binder_alloc_free_buf(&target_proc->alloc, t->buffer);
|
||||
|
@ -3104,13 +3142,14 @@ err_bad_call_stack:
|
|||
err_empty_call_stack:
|
||||
err_dead_binder:
|
||||
err_invalid_target_handle:
|
||||
err_no_context_mgr_node:
|
||||
if (target_thread)
|
||||
binder_thread_dec_tmpref(target_thread);
|
||||
if (target_proc)
|
||||
binder_proc_dec_tmpref(target_proc);
|
||||
if (target_node)
|
||||
if (target_node) {
|
||||
binder_dec_node(target_node, 1, 0);
|
||||
binder_dec_node_tmpref(target_node);
|
||||
}
|
||||
|
||||
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
|
||||
"%d:%d transaction failed %d/%d, size %lld-%lld line %d\n",
|
||||
|
|
|
@ -640,6 +640,7 @@ 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.
|
||||
|
@ -648,16 +649,15 @@ void vmbus_close(struct vmbus_channel *channel)
|
|||
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
||||
vmbus_close_internal(cur_channel);
|
||||
if (cur_channel->rescind) {
|
||||
mutex_lock(&vmbus_connection.channel_mutex);
|
||||
hv_process_channel_removal(cur_channel,
|
||||
hv_process_channel_removal(
|
||||
cur_channel->offermsg.child_relid);
|
||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Now close the primary.
|
||||
*/
|
||||
vmbus_close_internal(channel);
|
||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vmbus_close);
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ static void vmbus_rescind_cleanup(struct vmbus_channel *channel)
|
|||
|
||||
|
||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||
|
||||
channel->rescind = true;
|
||||
list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
|
||||
msglistentry) {
|
||||
|
||||
|
@ -381,14 +381,21 @@ static void vmbus_release_relid(u32 relid)
|
|||
true);
|
||||
}
|
||||
|
||||
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
|
||||
void hv_process_channel_removal(u32 relid)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct vmbus_channel *primary_channel;
|
||||
struct vmbus_channel *primary_channel, *channel;
|
||||
|
||||
BUG_ON(!channel->rescind);
|
||||
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
|
||||
|
||||
/*
|
||||
* Make sure channel is valid as we may have raced.
|
||||
*/
|
||||
channel = relid2channel(relid);
|
||||
if (!channel)
|
||||
return;
|
||||
|
||||
BUG_ON(!channel->rescind);
|
||||
if (channel->target_cpu != get_cpu()) {
|
||||
put_cpu();
|
||||
smp_call_function_single(channel->target_cpu,
|
||||
|
@ -515,6 +522,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
|||
if (!fnew) {
|
||||
if (channel->sc_creation_callback != NULL)
|
||||
channel->sc_creation_callback(newchannel);
|
||||
newchannel->probe_done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -834,7 +842,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
|||
{
|
||||
struct vmbus_channel_rescind_offer *rescind;
|
||||
struct vmbus_channel *channel;
|
||||
unsigned long flags;
|
||||
struct device *dev;
|
||||
|
||||
rescind = (struct vmbus_channel_rescind_offer *)hdr;
|
||||
|
@ -873,16 +880,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
|||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&channel->lock, flags);
|
||||
channel->rescind = true;
|
||||
spin_unlock_irqrestore(&channel->lock, flags);
|
||||
|
||||
/*
|
||||
* Now that we have posted the rescind state, perform
|
||||
* rescind related cleanup.
|
||||
*/
|
||||
vmbus_rescind_cleanup(channel);
|
||||
|
||||
/*
|
||||
* Now wait for offer handling to complete.
|
||||
*/
|
||||
|
@ -901,6 +898,7 @@ 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;
|
||||
}
|
||||
/*
|
||||
|
@ -909,6 +907,7 @@ 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);
|
||||
}
|
||||
|
@ -921,16 +920,16 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
|||
* 1. Close all sub-channels first
|
||||
* 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.
|
||||
*/
|
||||
mutex_lock(&vmbus_connection.channel_mutex);
|
||||
hv_process_channel_removal(channel,
|
||||
channel->offermsg.child_relid);
|
||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||
hv_process_channel_removal(rescind->child_relid);
|
||||
}
|
||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -768,8 +768,7 @@ static void vmbus_device_release(struct device *device)
|
|||
struct vmbus_channel *channel = hv_dev->channel;
|
||||
|
||||
mutex_lock(&vmbus_connection.channel_mutex);
|
||||
hv_process_channel_removal(channel,
|
||||
channel->offermsg.child_relid);
|
||||
hv_process_channel_removal(channel->offermsg.child_relid);
|
||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||
kfree(hv_dev);
|
||||
|
||||
|
|
|
@ -127,6 +127,8 @@
|
|||
#define MEI_DEV_ID_BXT_M 0x1A9A /* Broxton M */
|
||||
#define MEI_DEV_ID_APL_I 0x5A9A /* Apollo Lake I */
|
||||
|
||||
#define MEI_DEV_ID_GLK 0x319A /* Gemini Lake */
|
||||
|
||||
#define MEI_DEV_ID_KBP 0xA2BA /* Kaby Point */
|
||||
#define MEI_DEV_ID_KBP_2 0xA2BB /* Kaby Point 2 */
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
|
|||
{MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, MEI_ME_PCH8_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, MEI_ME_PCH8_CFG)},
|
||||
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_GLK, MEI_ME_PCH8_CFG)},
|
||||
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)},
|
||||
{MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)},
|
||||
|
||||
|
@ -226,12 +228,15 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME;
|
||||
|
||||
/*
|
||||
* For not wake-able HW runtime pm framework
|
||||
* can't be used on pci device level.
|
||||
* Use domain runtime pm callbacks instead.
|
||||
*/
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_me_set_pm_domain(dev);
|
||||
* ME maps runtime suspend/resume to D0i states,
|
||||
* hence we need to go around native PCI runtime service which
|
||||
* eventually brings the device into D3cold/hot state,
|
||||
* but the mei device cannot wake up from D3 unlike from D0i3.
|
||||
* To get around the PCI device native runtime pm,
|
||||
* ME uses runtime pm domain handlers which take precedence
|
||||
* over the driver's pm handlers.
|
||||
*/
|
||||
mei_me_set_pm_domain(dev);
|
||||
|
||||
if (mei_pg_is_enabled(dev))
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
@ -271,8 +276,7 @@ static void mei_me_shutdown(struct pci_dev *pdev)
|
|||
dev_dbg(&pdev->dev, "shutdown\n");
|
||||
mei_stop(dev);
|
||||
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_me_unset_pm_domain(dev);
|
||||
mei_me_unset_pm_domain(dev);
|
||||
|
||||
mei_disable_interrupts(dev);
|
||||
free_irq(pdev->irq, dev);
|
||||
|
@ -300,8 +304,7 @@ static void mei_me_remove(struct pci_dev *pdev)
|
|||
dev_dbg(&pdev->dev, "stop\n");
|
||||
mei_stop(dev);
|
||||
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_me_unset_pm_domain(dev);
|
||||
mei_me_unset_pm_domain(dev);
|
||||
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
|
|
|
@ -144,12 +144,14 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME;
|
||||
|
||||
/*
|
||||
* For not wake-able HW runtime pm framework
|
||||
* can't be used on pci device level.
|
||||
* Use domain runtime pm callbacks instead.
|
||||
*/
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_txe_set_pm_domain(dev);
|
||||
* TXE maps runtime suspend/resume to own power gating states,
|
||||
* hence we need to go around native PCI runtime service which
|
||||
* eventually brings the device into D3cold/hot state.
|
||||
* But the TXE device cannot wake up from D3 unlike from own
|
||||
* power gating. To get around PCI device native runtime pm,
|
||||
* TXE uses runtime pm domain handlers which take precedence.
|
||||
*/
|
||||
mei_txe_set_pm_domain(dev);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
|
@ -186,8 +188,7 @@ static void mei_txe_shutdown(struct pci_dev *pdev)
|
|||
dev_dbg(&pdev->dev, "shutdown\n");
|
||||
mei_stop(dev);
|
||||
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_txe_unset_pm_domain(dev);
|
||||
mei_txe_unset_pm_domain(dev);
|
||||
|
||||
mei_disable_interrupts(dev);
|
||||
free_irq(pdev->irq, dev);
|
||||
|
@ -215,8 +216,7 @@ static void mei_txe_remove(struct pci_dev *pdev)
|
|||
|
||||
mei_stop(dev);
|
||||
|
||||
if (!pci_dev_run_wake(pdev))
|
||||
mei_txe_unset_pm_domain(dev);
|
||||
mei_txe_unset_pm_domain(dev);
|
||||
|
||||
mei_disable_interrupts(dev);
|
||||
free_irq(pdev->irq, dev);
|
||||
|
@ -318,15 +318,7 @@ static int mei_txe_pm_runtime_suspend(struct device *device)
|
|||
else
|
||||
ret = -EAGAIN;
|
||||
|
||||
/*
|
||||
* If everything is okay we're about to enter PCI low
|
||||
* power state (D3) therefor we need to disable the
|
||||
* interrupts towards host.
|
||||
* However if device is not wakeable we do not enter
|
||||
* D-low state and we need to keep the interrupt kicking
|
||||
*/
|
||||
if (!ret && pci_dev_run_wake(pdev))
|
||||
mei_disable_interrupts(dev);
|
||||
/* keep irq on we are staying in D0 */
|
||||
|
||||
dev_dbg(&pdev->dev, "rpm: txe: runtime suspend ret=%d\n", ret);
|
||||
|
||||
|
|
|
@ -1403,7 +1403,7 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
|
|||
const int *srv_version, int srv_vercnt,
|
||||
int *nego_fw_version, int *nego_srv_version);
|
||||
|
||||
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid);
|
||||
void hv_process_channel_removal(u32 relid);
|
||||
|
||||
void vmbus_setevent(struct vmbus_channel *channel);
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue