From 50ad39b28e61adb3d9da178c47e41100f554adeb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 Jan 2010 03:16:34 -0800 Subject: core: protect against modem removal in critical callbacks (rh #553953) There are more places to handle, but these are the most critical. If the modem is removed while a command is in-progress, the mm-callback-info code will set info->modem to NULL. Make sure we check for that in callbacks and return a reasonable error. Previous code would just blindly forge ahead and die on a null dereference. --- src/mm-modem.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'src/mm-modem.c') diff --git a/src/mm-modem.c b/src/mm-modem.c index 5f0f5fac..27b77954 100644 --- a/src/mm-modem.c +++ b/src/mm-modem.c @@ -11,7 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2008 - 2009 Novell, Inc. - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009 - 2010 Red Hat, Inc. */ #include @@ -29,6 +29,28 @@ static void impl_modem_get_info (MMModem *modem, DBusGMethodInvocation *context) #include "mm-modem-glue.h" +/* Should be used from callbacks to check whether the modem was removed after + * the callback's operation was started, but before the callback itself was + * called, in which case the MMModem passed to the callback is NULL. + */ +GError * +mm_modem_check_removed (MMModem *self, const GError *error) +{ + if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) + return g_error_copy (error); + + if (!self) { + /* If the modem was NULL, the error *should* have been + * MM_MODEM_ERROR_REMOVED. If it wasn't, make it that. + */ + return g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + } + + return NULL; +} + static void async_op_not_supported (MMModem *self, MMModemFn callback, @@ -92,7 +114,6 @@ finish_disable (MMModem *self, MMModemFn callback, gpointer user_data) { - if (MM_MODEM_GET_INTERFACE (self)->disable) MM_MODEM_GET_INTERFACE (self)->disable (self, callback, user_data); else @@ -110,9 +131,27 @@ disable_disconnect_done (MMModem *self, gpointer user_data) { DisableDisconnectInfo *cb_data = user_data; + GError *tmp_error = NULL; + + /* Check for modem removal */ + if (g_error_matches (error, MM_MODEM_ERROR, MM_MODEM_ERROR_REMOVED)) + tmp_error = g_error_copy (error); + else if (!self) { + tmp_error = g_error_new_literal (MM_MODEM_ERROR, + MM_MODEM_ERROR_REMOVED, + "The modem was removed."); + } + + /* And send an immediate error reply if the modem was removed */ + if (tmp_error) { + cb_data->callback (NULL, tmp_error, cb_data->user_data); + g_free (cb_data); + g_error_free (tmp_error); + return; + } - /* ignore errors */ if (error) { + /* Don't really care what the error was; log it and proceed to disable */ g_warning ("%s: (%s): error disconnecting the modem while disabling: (%d) %s", __func__, mm_modem_get_device (self), @@ -337,7 +376,6 @@ mm_modem_disconnect (MMModem *self, return; } - if (MM_MODEM_GET_INTERFACE (self)->disconnect) MM_MODEM_GET_INTERFACE (self)->disconnect (self, callback, user_data); else -- cgit v1.2.3-70-g09d2