diff options
author | Tilman Schmidt <tilman@imap.cc> | 2014-10-11 07:46:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-10-14 15:05:34 -0400 |
commit | b8324f94202af7dc688576259803a2ef9a98d655 (patch) | |
tree | 05043b6668f823104c3f5eb29451a3b4f0941efd /drivers | |
parent | 846ac30135e7c5e03b487c16c87ccb1ab020a01f (diff) |
isdn/gigaset: fix non-heap pointer deallocation
at_state structures may be allocated individually or as part of a
cardstate or bc_state structure. The disconnect() function handled
both cases, creating a risk that it might try to deallocate an
at_state structure that had not been allocated individually.
Fix by splitting disconnect() into two variants handling cases
with and without an associated B channel separately, and adding
an explicit check.
Spotted with Coverity.
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/isdn/gigaset/ev-layer.c | 111 |
1 files changed, 70 insertions, 41 deletions
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index 0f699ebd81c5..c8ced12fa452 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c | |||
@@ -604,14 +604,14 @@ void gigaset_handle_modem_response(struct cardstate *cs) | |||
604 | } | 604 | } |
605 | EXPORT_SYMBOL_GPL(gigaset_handle_modem_response); | 605 | EXPORT_SYMBOL_GPL(gigaset_handle_modem_response); |
606 | 606 | ||
607 | /* disconnect | 607 | /* disconnect_nobc |
608 | * process closing of connection associated with given AT state structure | 608 | * process closing of connection associated with given AT state structure |
609 | * without B channel | ||
609 | */ | 610 | */ |
610 | static void disconnect(struct at_state_t **at_state_p) | 611 | static void disconnect_nobc(struct at_state_t **at_state_p, |
612 | struct cardstate *cs) | ||
611 | { | 613 | { |
612 | unsigned long flags; | 614 | unsigned long flags; |
613 | struct bc_state *bcs = (*at_state_p)->bcs; | ||
614 | struct cardstate *cs = (*at_state_p)->cs; | ||
615 | 615 | ||
616 | spin_lock_irqsave(&cs->lock, flags); | 616 | spin_lock_irqsave(&cs->lock, flags); |
617 | ++(*at_state_p)->seq_index; | 617 | ++(*at_state_p)->seq_index; |
@@ -622,23 +622,44 @@ static void disconnect(struct at_state_t **at_state_p) | |||
622 | gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE"); | 622 | gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE"); |
623 | cs->commands_pending = 1; | 623 | cs->commands_pending = 1; |
624 | } | 624 | } |
625 | spin_unlock_irqrestore(&cs->lock, flags); | ||
626 | 625 | ||
627 | if (bcs) { | 626 | /* check for and deallocate temporary AT state */ |
628 | /* B channel assigned: invoke hardware specific handler */ | 627 | if (!list_empty(&(*at_state_p)->list)) { |
629 | cs->ops->close_bchannel(bcs); | ||
630 | /* notify LL */ | ||
631 | if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { | ||
632 | bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); | ||
633 | gigaset_isdn_hupD(bcs); | ||
634 | } | ||
635 | } else { | ||
636 | /* no B channel assigned: just deallocate */ | ||
637 | spin_lock_irqsave(&cs->lock, flags); | ||
638 | list_del(&(*at_state_p)->list); | 628 | list_del(&(*at_state_p)->list); |
639 | kfree(*at_state_p); | 629 | kfree(*at_state_p); |
640 | *at_state_p = NULL; | 630 | *at_state_p = NULL; |
641 | spin_unlock_irqrestore(&cs->lock, flags); | 631 | } |
632 | |||
633 | spin_unlock_irqrestore(&cs->lock, flags); | ||
634 | } | ||
635 | |||
636 | /* disconnect_bc | ||
637 | * process closing of connection associated with given AT state structure | ||
638 | * and B channel | ||
639 | */ | ||
640 | static void disconnect_bc(struct at_state_t *at_state, | ||
641 | struct cardstate *cs, struct bc_state *bcs) | ||
642 | { | ||
643 | unsigned long flags; | ||
644 | |||
645 | spin_lock_irqsave(&cs->lock, flags); | ||
646 | ++at_state->seq_index; | ||
647 | |||
648 | /* revert to selected idle mode */ | ||
649 | if (!cs->cidmode) { | ||
650 | cs->at_state.pending_commands |= PC_UMMODE; | ||
651 | gig_dbg(DEBUG_EVENT, "Scheduling PC_UMMODE"); | ||
652 | cs->commands_pending = 1; | ||
653 | } | ||
654 | spin_unlock_irqrestore(&cs->lock, flags); | ||
655 | |||
656 | /* invoke hardware specific handler */ | ||
657 | cs->ops->close_bchannel(bcs); | ||
658 | |||
659 | /* notify LL */ | ||
660 | if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { | ||
661 | bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); | ||
662 | gigaset_isdn_hupD(bcs); | ||
642 | } | 663 | } |
643 | } | 664 | } |
644 | 665 | ||
@@ -646,7 +667,7 @@ static void disconnect(struct at_state_t **at_state_p) | |||
646 | * get a free AT state structure: either one of those associated with the | 667 | * get a free AT state structure: either one of those associated with the |
647 | * B channels of the Gigaset device, or if none of those is available, | 668 | * B channels of the Gigaset device, or if none of those is available, |
648 | * a newly allocated one with bcs=NULL | 669 | * a newly allocated one with bcs=NULL |
649 | * The structure should be freed by calling disconnect() after use. | 670 | * The structure should be freed by calling disconnect_nobc() after use. |
650 | */ | 671 | */ |
651 | static inline struct at_state_t *get_free_channel(struct cardstate *cs, | 672 | static inline struct at_state_t *get_free_channel(struct cardstate *cs, |
652 | int cid) | 673 | int cid) |
@@ -1057,7 +1078,7 @@ static void do_action(int action, struct cardstate *cs, | |||
1057 | struct event_t *ev) | 1078 | struct event_t *ev) |
1058 | { | 1079 | { |
1059 | struct at_state_t *at_state = *p_at_state; | 1080 | struct at_state_t *at_state = *p_at_state; |
1060 | struct at_state_t *at_state2; | 1081 | struct bc_state *bcs2; |
1061 | unsigned long flags; | 1082 | unsigned long flags; |
1062 | 1083 | ||
1063 | int channel; | 1084 | int channel; |
@@ -1156,8 +1177,8 @@ static void do_action(int action, struct cardstate *cs, | |||
1156 | break; | 1177 | break; |
1157 | case ACT_RING: | 1178 | case ACT_RING: |
1158 | /* get fresh AT state structure for new CID */ | 1179 | /* get fresh AT state structure for new CID */ |
1159 | at_state2 = get_free_channel(cs, ev->parameter); | 1180 | at_state = get_free_channel(cs, ev->parameter); |
1160 | if (!at_state2) { | 1181 | if (!at_state) { |
1161 | dev_warn(cs->dev, | 1182 | dev_warn(cs->dev, |
1162 | "RING ignored: could not allocate channel structure\n"); | 1183 | "RING ignored: could not allocate channel structure\n"); |
1163 | break; | 1184 | break; |
@@ -1166,16 +1187,16 @@ static void do_action(int action, struct cardstate *cs, | |||
1166 | /* initialize AT state structure | 1187 | /* initialize AT state structure |
1167 | * note that bcs may be NULL if no B channel is free | 1188 | * note that bcs may be NULL if no B channel is free |
1168 | */ | 1189 | */ |
1169 | at_state2->ConState = 700; | 1190 | at_state->ConState = 700; |
1170 | for (i = 0; i < STR_NUM; ++i) { | 1191 | for (i = 0; i < STR_NUM; ++i) { |
1171 | kfree(at_state2->str_var[i]); | 1192 | kfree(at_state->str_var[i]); |
1172 | at_state2->str_var[i] = NULL; | 1193 | at_state->str_var[i] = NULL; |
1173 | } | 1194 | } |
1174 | at_state2->int_var[VAR_ZCTP] = -1; | 1195 | at_state->int_var[VAR_ZCTP] = -1; |
1175 | 1196 | ||
1176 | spin_lock_irqsave(&cs->lock, flags); | 1197 | spin_lock_irqsave(&cs->lock, flags); |
1177 | at_state2->timer_expires = RING_TIMEOUT; | 1198 | at_state->timer_expires = RING_TIMEOUT; |
1178 | at_state2->timer_active = 1; | 1199 | at_state->timer_active = 1; |
1179 | spin_unlock_irqrestore(&cs->lock, flags); | 1200 | spin_unlock_irqrestore(&cs->lock, flags); |
1180 | break; | 1201 | break; |
1181 | case ACT_ICALL: | 1202 | case ACT_ICALL: |
@@ -1213,14 +1234,17 @@ static void do_action(int action, struct cardstate *cs, | |||
1213 | case ACT_DISCONNECT: | 1234 | case ACT_DISCONNECT: |
1214 | cs->cur_at_seq = SEQ_NONE; | 1235 | cs->cur_at_seq = SEQ_NONE; |
1215 | at_state->cid = -1; | 1236 | at_state->cid = -1; |
1216 | if (bcs && cs->onechannel && cs->dle) { | 1237 | if (!bcs) { |
1238 | disconnect_nobc(p_at_state, cs); | ||
1239 | } else if (cs->onechannel && cs->dle) { | ||
1217 | /* Check for other open channels not needed: | 1240 | /* Check for other open channels not needed: |
1218 | * DLE only used for M10x with one B channel. | 1241 | * DLE only used for M10x with one B channel. |
1219 | */ | 1242 | */ |
1220 | at_state->pending_commands |= PC_DLE0; | 1243 | at_state->pending_commands |= PC_DLE0; |
1221 | cs->commands_pending = 1; | 1244 | cs->commands_pending = 1; |
1222 | } else | 1245 | } else { |
1223 | disconnect(p_at_state); | 1246 | disconnect_bc(at_state, cs, bcs); |
1247 | } | ||
1224 | break; | 1248 | break; |
1225 | case ACT_FAKEDLE0: | 1249 | case ACT_FAKEDLE0: |
1226 | at_state->int_var[VAR_ZDLE] = 0; | 1250 | at_state->int_var[VAR_ZDLE] = 0; |
@@ -1228,25 +1252,27 @@ static void do_action(int action, struct cardstate *cs, | |||
1228 | /* fall through */ | 1252 | /* fall through */ |
1229 | case ACT_DLE0: | 1253 | case ACT_DLE0: |
1230 | cs->cur_at_seq = SEQ_NONE; | 1254 | cs->cur_at_seq = SEQ_NONE; |
1231 | at_state2 = &cs->bcs[cs->curchannel].at_state; | 1255 | bcs2 = cs->bcs + cs->curchannel; |
1232 | disconnect(&at_state2); | 1256 | disconnect_bc(&bcs2->at_state, cs, bcs2); |
1233 | break; | 1257 | break; |
1234 | case ACT_ABORTHUP: | 1258 | case ACT_ABORTHUP: |
1235 | cs->cur_at_seq = SEQ_NONE; | 1259 | cs->cur_at_seq = SEQ_NONE; |
1236 | dev_warn(cs->dev, "Could not hang up.\n"); | 1260 | dev_warn(cs->dev, "Could not hang up.\n"); |
1237 | at_state->cid = -1; | 1261 | at_state->cid = -1; |
1238 | if (bcs && cs->onechannel) | 1262 | if (!bcs) |
1263 | disconnect_nobc(p_at_state, cs); | ||
1264 | else if (cs->onechannel) | ||
1239 | at_state->pending_commands |= PC_DLE0; | 1265 | at_state->pending_commands |= PC_DLE0; |
1240 | else | 1266 | else |
1241 | disconnect(p_at_state); | 1267 | disconnect_bc(at_state, cs, bcs); |
1242 | schedule_init(cs, MS_RECOVER); | 1268 | schedule_init(cs, MS_RECOVER); |
1243 | break; | 1269 | break; |
1244 | case ACT_FAILDLE0: | 1270 | case ACT_FAILDLE0: |
1245 | cs->cur_at_seq = SEQ_NONE; | 1271 | cs->cur_at_seq = SEQ_NONE; |
1246 | dev_warn(cs->dev, "Error leaving DLE mode.\n"); | 1272 | dev_warn(cs->dev, "Error leaving DLE mode.\n"); |
1247 | cs->dle = 0; | 1273 | cs->dle = 0; |
1248 | at_state2 = &cs->bcs[cs->curchannel].at_state; | 1274 | bcs2 = cs->bcs + cs->curchannel; |
1249 | disconnect(&at_state2); | 1275 | disconnect_bc(&bcs2->at_state, cs, bcs2); |
1250 | schedule_init(cs, MS_RECOVER); | 1276 | schedule_init(cs, MS_RECOVER); |
1251 | break; | 1277 | break; |
1252 | case ACT_FAILDLE1: | 1278 | case ACT_FAILDLE1: |
@@ -1275,14 +1301,14 @@ static void do_action(int action, struct cardstate *cs, | |||
1275 | if (reinit_and_retry(cs, channel) < 0) { | 1301 | if (reinit_and_retry(cs, channel) < 0) { |
1276 | dev_warn(cs->dev, | 1302 | dev_warn(cs->dev, |
1277 | "Could not get a call ID. Cannot dial.\n"); | 1303 | "Could not get a call ID. Cannot dial.\n"); |
1278 | at_state2 = &cs->bcs[channel].at_state; | 1304 | bcs2 = cs->bcs + channel; |
1279 | disconnect(&at_state2); | 1305 | disconnect_bc(&bcs2->at_state, cs, bcs2); |
1280 | } | 1306 | } |
1281 | break; | 1307 | break; |
1282 | case ACT_ABORTCID: | 1308 | case ACT_ABORTCID: |
1283 | cs->cur_at_seq = SEQ_NONE; | 1309 | cs->cur_at_seq = SEQ_NONE; |
1284 | at_state2 = &cs->bcs[cs->curchannel].at_state; | 1310 | bcs2 = cs->bcs + cs->curchannel; |
1285 | disconnect(&at_state2); | 1311 | disconnect_bc(&bcs2->at_state, cs, bcs2); |
1286 | break; | 1312 | break; |
1287 | 1313 | ||
1288 | case ACT_DIALING: | 1314 | case ACT_DIALING: |
@@ -1291,7 +1317,10 @@ static void do_action(int action, struct cardstate *cs, | |||
1291 | break; | 1317 | break; |
1292 | 1318 | ||
1293 | case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL procssng */ | 1319 | case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL procssng */ |
1294 | disconnect(p_at_state); | 1320 | if (bcs) |
1321 | disconnect_bc(at_state, cs, bcs); | ||
1322 | else | ||
1323 | disconnect_nobc(p_at_state, cs); | ||
1295 | break; | 1324 | break; |
1296 | 1325 | ||
1297 | case ACT_ABORTDIAL: /* error/timeout during dial preparation */ | 1326 | case ACT_ABORTDIAL: /* error/timeout during dial preparation */ |