cpuidle: preventive check in cpuidle_select against crash
PATCH 1/1 cpuidle: preventive check in cpuidle_select against crash
When selecting the idle state using cpuidle_select, there is no check on cpuidle_curr_governor. In cpuidle_switch_governor, cpuidle_currr_governor can be set to NULL to specify "disabled".
Since cpuidle_select cannot return negative value, it has to return 0 in case of error. Printing logs and returning can help in debugging and preventing possible kernel crash scenarios.
---
drivers/cpuidle/cpuidle.c +13 -0
@@ -268,6 +268,19 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,*/ int cpuidle_select (struct cpuidle_driver *drv,struct cpuidle_device *dev) { + +/* Since negative return is not allowed +* we have to return 0 even if the +* framework cannot select the idle state +*/ +if (!cpuidle_curr_governor) { +pr_err ("idle governor is disabled\n" ); +return 0 ; + } +if (!cpuidle_curr_governor->select) { +pr_err ("idle governor select is NULL\n" ); +return 0 ; + }return cpuidle_curr_governor->select (drv, dev); }
Rafael J. Wysocki
maintainerQuoted 3 lines · gaurav jindal
When selecting the idle state using
cpuidle_select, there is no check oncpuidle_curr_governor. Incpuidle_switch_governor,cpuidle_currr_governorcan be set to NULL to specify "disabled".
How exactly?
Quoted 7 lines · Rafael J. Wysocki
Quoted 3 lines · gaurav jindal
When selecting the idle state using
cpuidle_select, there is no check oncpuidle_curr_governor. Incpuidle_switch_governor,cpuidle_currr_governorcan be set to NULL to specify "disabled".How exactly?
In cpuidle_switch_governor:
/**
* cpuidle_switch_governor - changes the governor
* @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
* Must be called with cpuidle_lock acquired.
*/
int cpuidle_switch_governor(struct cpuidle_governor *gov)
{
struct cpuidle_device *dev;
if (gov == cpuidle_curr_governor)
return 0;
cpuidle_uninstall_idle_handler();
if (cpuidle_curr_governor) {
list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
cpuidle_disable_device(dev);
}
cpuidle_curr_governor = gov;This allows to set the cpuidle_switch_governor as NULL. Although there is no current code flow leading here, but it has a potential for bug in future. So may be better to have prevention.
Rafael J. Wysocki
maintainerQuoted 34 lines · gaurav jindal
Quoted 7 lines · Rafael J. Wysocki
Quoted 3 lines · gaurav jindal
When selecting the idle state using
cpuidle_select, there is no check oncpuidle_curr_governor. Incpuidle_switch_governor,cpuidle_currr_governorcan be set to NULL to specify "disabled".How exactly?
In
cpuidle_switch_governor:/**
*cpuidle_switch_governor- changes the governor
* @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
* Must be called withcpuidle_lockacquired.
*/
intcpuidle_switch_governor(struct cpuidle_governor *gov){ struct cpuidle_device *dev; if (gov == cpuidle_curr_governor) return 0; cpuidle_uninstall_idle_handler(); if (cpuidle_curr_governor) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_disable_device(dev); } cpuidle_curr_governor = gov;
Quoted 3 lines
This allows to set the
cpuidle_switch_governoras NULL. Although there is no current code flow leading here, but it has a potential for bug in future. So may be better to have prevention.
Or maybe not.
Why don't you make cpuidle_switch_governor() check the argument against NULL instead?
Quoted 36 lines · Rafael J. Wysocki
Quoted 34 lines · gaurav jindal
Quoted 7 lines · Rafael J. Wysocki
Quoted 3 lines · gaurav jindal
When selecting the idle state using
cpuidle_select, there is no check oncpuidle_curr_governor. Incpuidle_switch_governor,cpuidle_currr_governorcan be set to NULL to specify "disabled".How exactly?
In
cpuidle_switch_governor:/**
*cpuidle_switch_governor- changes the governor
* @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
* Must be called withcpuidle_lockacquired.
*/
intcpuidle_switch_governor(struct cpuidle_governor *gov){ struct cpuidle_device *dev; if (gov == cpuidle_curr_governor) return 0; cpuidle_uninstall_idle_handler(); if (cpuidle_curr_governor) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_disable_device(dev); } cpuidle_curr_governor = gov;
Quoted 8 lines
Quoted 3 lines
This allows to set the
cpuidle_switch_governoras NULL. Although there is no current code flow leading here, but it has a potential for bug in future. So may be better to have prevention.Or maybe not.
Why don't you make
cpuidle_switch_governor()check the argument against NULL instead?
If we check gov (argument passed in cpuidle_switch_governor())against NULL in cpuidle_switch_governor, can be a problem in a case where it is called ascpuidle_switch_governor(NULL);
If cpuidle_curr_governor is not NULL, first the device is disabled.
if (cpuidle_curr_governor) {
list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
cpuidle_disable_device(dev);
}after this cpuidle_curr_governor is set to gov, which is NULL in this case.
cpuidle_curr_governor = gov;
/* if is not updated by inserting a check, it will have an oudated value*/
Now, if gov is not NULL (which it is in this case), cpuidle device is enabled
if (gov) {
list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
cpuidle_enable_device(dev);
cpuidle_install_idle_handler();
printk(KERN_INFO "cpuidle: using governor %s\n", gov->name);
}If we check for gov against NULL in this function, it will produce dangling pointers and resource leaks.
Quoted 65 lines · CET gaurav jindal
Quoted 36 lines · Rafael J. Wysocki
Quoted 34 lines · gaurav jindal
Quoted 7 lines · Rafael J. Wysocki
Quoted 3 lines · gaurav jindal
When selecting the idle state using
cpuidle_select, there is no check oncpuidle_curr_governor. Incpuidle_switch_governor,cpuidle_currr_governorcan be set to NULL to specify "disabled".How exactly?
In
cpuidle_switch_governor:/**
*cpuidle_switch_governor- changes the governor
* @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
* Must be called withcpuidle_lockacquired.
*/
intcpuidle_switch_governor(struct cpuidle_governor *gov){ struct cpuidle_device *dev; if (gov == cpuidle_curr_governor) return 0; cpuidle_uninstall_idle_handler(); if (cpuidle_curr_governor) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_disable_device(dev); } cpuidle_curr_governor = gov;Quoted 8 lines
Quoted 3 lines
This allows to set the
cpuidle_switch_governoras NULL. Although there is no current code flow leading here, but it has a potential for bug in future. So may be better to have prevention.Or maybe not.
Why don't you make
cpuidle_switch_governor()check the argument against NULL instead?If we check gov (argument passed in cpuidle_switch_governor())against NULL in
cpuidle_switch_governor, can be a problem in a case where it is called ascpuidle_switch_governor(NULL);If
cpuidle_curr_governoris not NULL, first the device is disabled.if (cpuidle_curr_governor) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_disable_device(dev); }after this
cpuidle_curr_governoris set to gov, which is NULL in this case.
cpuidle_curr_governor= gov;
/* if is not updated by inserting a check, it will have an oudated value*/Now, if gov is not NULL (which it is in this case), cpuidle device is enabled
Quoted 8 lines
if (gov) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_enable_device(dev); cpuidle_install_idle_handler(); printk(KERN_INFO "cpuidle: using governor %s\n", gov->name); }If we check for gov against NULL in this function, it will produce dangling pointers and resource leaks.
I didn't recommend you to introduce bugs.
Just return -EINVAL if gov is NULL before checking if gov is equal to cpuidle_curr_governor.
Thanks,
Rafael
Quoted 75 lines · Rafael J. Wysocki
Quoted 65 lines · CET gaurav jindal
Quoted 36 lines · Rafael J. Wysocki
Quoted 34 lines · gaurav jindal
Quoted 7 lines · Rafael J. Wysocki
Quoted 3 lines · gaurav jindal
When selecting the idle state using
cpuidle_select, there is no check oncpuidle_curr_governor. Incpuidle_switch_governor,cpuidle_currr_governorcan be set to NULL to specify "disabled".How exactly?
In
cpuidle_switch_governor:/**
*cpuidle_switch_governor- changes the governor
* @gov: the new target governor
*
* NOTE: "gov" can be NULL to specify disabled
* Must be called withcpuidle_lockacquired.
*/
intcpuidle_switch_governor(struct cpuidle_governor *gov){ struct cpuidle_device *dev; if (gov == cpuidle_curr_governor) return 0; cpuidle_uninstall_idle_handler(); if (cpuidle_curr_governor) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_disable_device(dev); } cpuidle_curr_governor = gov;Quoted 8 lines
Quoted 3 lines
This allows to set the
cpuidle_switch_governoras NULL. Although there is no current code flow leading here, but it has a potential for bug in future. So may be better to have prevention.Or maybe not.
Why don't you make
cpuidle_switch_governor()check the argument against NULL instead?If we check gov (argument passed in cpuidle_switch_governor())against NULL in
cpuidle_switch_governor, can be a problem in a case where it is called ascpuidle_switch_governor(NULL);If
cpuidle_curr_governoris not NULL, first the device is disabled.if (cpuidle_curr_governor) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_disable_device(dev); }after this
cpuidle_curr_governoris set to gov, which is NULL in this case.
cpuidle_curr_governor= gov;
/* if is not updated by inserting a check, it will have an oudated value*/Now, if gov is not NULL (which it is in this case), cpuidle device is enabled
Quoted 8 lines
if (gov) { list_for_each_entry(dev, &cpuidle_detected_devices, device_list) cpuidle_enable_device(dev); cpuidle_install_idle_handler(); printk(KERN_INFO "cpuidle: using governor %s\n", gov->name); }If we check for gov against NULL in this function, it will produce dangling pointers and resource leaks.
Quoted 2 lines
I didn't recommend you to introduce bugs.
I did not intend to do so. I am really sorry it got expressed in that way :(.
Quoted 3 lines
Just return
-EINVALif gov is NULL before checking if gov is equal tocpuidle_curr_governor.
Okay
Quoted 3 lines
Thanks,
Rafael
this patch checks if the new governor is NULL before updating the cupidle_curr_governor.
---
drivers/cpuidle/governor.c +3 -2
@@ -36,14 +36,15 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)/** * cpuidle_switch_governor - changes the governor * @gov: the new target governor -* -* NOTE: "gov" can be NULL to specify disabled * Must be called with cpuidle_lock acquired. */ int cpuidle_switch_governor (struct cpuidle_governor *gov) {struct cpuidle_device *dev; +if (!gov) +return -EINVAL ; +if (gov == cpuidle_curr_governor)return 0 ;