clk: check ->determine/round_rate() return value in clk_calc_new_rates
authorBoris Brezillon <boris.brezillon@free-electrons.com>
Sun, 29 Mar 2015 01:48:48 +0000 (03:48 +0200)
committerMichael Turquette <mturquette@linaro.org>
Mon, 13 Apr 2015 04:09:49 +0000 (21:09 -0700)
->determine_rate() and ->round_rate() can return the closest rate to the
requested one or an error code.
clk_calc_new_rates is assuming these functions can't return a negative
value, which leads to a undefined behavior when the clk implementation
returns such an error code.
Fix this by returning NULL in case ->determine_rate() or ->round_rate()
returned an error code.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Michael Turquette <mturquette@linaro.org>
drivers/clk/clk.c

index fa5a00e5ee41b5261e323ba5602528f6fbe8582e..459ce9da13e0631b41e6cafc5ed9a85523e5e1b7 100644 (file)
@@ -1618,6 +1618,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk,
        unsigned long min_rate;
        unsigned long max_rate;
        int p_index = 0;
+       long ret;
 
        /* sanity */
        if (IS_ERR_OR_NULL(clk))
@@ -1633,15 +1634,23 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk,
        /* find the closest rate and parent clk/rate */
        if (clk->ops->determine_rate) {
                parent_hw = parent ? parent->hw : NULL;
-               new_rate = clk->ops->determine_rate(clk->hw, rate,
-                                                   min_rate,
-                                                   max_rate,
-                                                   &best_parent_rate,
-                                                   &parent_hw);
+               ret = clk->ops->determine_rate(clk->hw, rate,
+                                              min_rate,
+                                              max_rate,
+                                              &best_parent_rate,
+                                              &parent_hw);
+               if (ret < 0)
+                       return NULL;
+
+               new_rate = ret;
                parent = parent_hw ? parent_hw->core : NULL;
        } else if (clk->ops->round_rate) {
-               new_rate = clk->ops->round_rate(clk->hw, rate,
-                                               &best_parent_rate);
+               ret = clk->ops->round_rate(clk->hw, rate,
+                                          &best_parent_rate);
+               if (ret < 0)
+                       return NULL;
+
+               new_rate = ret;
                if (new_rate < min_rate || new_rate > max_rate)
                        return NULL;
        } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) {