From 4dba5345e14c61b041c0410b3d6614575515a57a Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Fri, 2 Jun 2017 11:38:58 -0700 Subject: [PATCH] power: qpnp-fg-gen3: Fix possible integer overflow Currently, DIV_ROUND_CLOSEST is used while finding the delta between initial and final cc_soc_sw in terms of percentage. This can go beyond 32 bits with the intermediate multiplication before it is down-converted back to less than 32 bits. Fix this. Also, when the cc_soc_sw delta is less than 50 %, discard the data so that the last learned capacity won't be updated. While at it, fix a similar use of DIV_ROUND_CLOSEST in another place fg_encode_default() where an integer overflow can happen if it is passed with large integer values. Change-Id: Ia2b0f5456663a90bcd32a4fed52d1e579cff4e1f Signed-off-by: Subbaraman Narayanamurthy --- drivers/power/supply/qcom/qpnp-fg-gen3.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index d0f7a5e1e227..6ef1eb379060 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -491,7 +491,7 @@ static void fg_encode_default(struct fg_sram_param *sp, int i, mask = 0xff; int64_t temp; - temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr); + temp = (int64_t)div_s64((s64)val * sp[id].numrtr, sp[id].denmtr); pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); for (i = 0; i < sp[id].len; i++) { buf[i] = temp & mask; @@ -1315,9 +1315,16 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) return rc; } - cc_soc_delta_pct = DIV_ROUND_CLOSEST( - abs(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, - CC_SOC_30BIT); + cc_soc_delta_pct = + div64_s64((int64_t)(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, + CC_SOC_30BIT); + + /* If the delta is < 50%, then skip processing full data */ + if (cc_soc_delta_pct < 50) { + pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct); + return -ERANGE; + } + delta_cc_uah = div64_s64(chip->cl.learned_cc_uah * cc_soc_delta_pct, 100); chip->cl.final_cc_uah = chip->cl.init_cc_uah + delta_cc_uah; @@ -1387,7 +1394,6 @@ out: return rc; } -#define FULL_SOC_RAW 255 static void fg_cap_learning_update(struct fg_chip *chip) { int rc, batt_soc, batt_soc_msb;