Bug 1077645 - Be paranoid when parsing external intent extras. r=snorp, a=sylvestre
authorRichard Newman <rnewman@mozilla.com>
Fri, 03 Oct 2014 23:32:28 -0700
changeset 258701 628f8f6c6f72
parent 258700 7683a98b0400
child 258702 96bcea5ee703
push id4700
push userryanvm@gmail.com
push date2015-04-21 23:53 +0000
treeherdermozilla-beta@d27c9211ebb3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp, sylvestre
bugs1077645
milestone33.0
Bug 1077645 - Be paranoid when parsing external intent extras. r=snorp, a=sylvestre
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/NotificationHelper.java
mobile/android/base/util/StringUtils.java
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -460,17 +460,17 @@ public class BrowserApp extends GeckoApp
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         mAboutHomeStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_ABOUTHOME");
 
         final Intent intent = getIntent();
 
-        String args = intent.getStringExtra("args");
+        String args = StringUtils.getStringExtra(intent, "args");
         if (args != null && args.contains(GUEST_BROWSING_ARG)) {
             mProfile = GeckoProfile.createGuestProfile(this);
         } else {
             GeckoProfile.maybeCleanupGuestProfile(this);
         }
 
         // This has to be prepared prior to calling GeckoApp.onCreate, because
         // widget code and BrowserToolbar need it, and they're created by the
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -49,16 +49,17 @@ import org.mozilla.gecko.updater.UpdateS
 import org.mozilla.gecko.util.ActivityResultHandler;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.FileUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSObject;
 import org.mozilla.gecko.util.PrefUtils;
+import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.webapp.EventListener;
 import org.mozilla.gecko.webapp.UninstallListener;
 import org.mozilla.gecko.widget.ButtonToast;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -1079,30 +1080,29 @@ public abstract class GeckoApp
 
                 if (Build.VERSION.SDK_INT >= 11)
                     window.getDecorView().setSystemUiVisibility(fullscreen ? 1 : 0);
             }
         });
     }
 
     /**
-     * Check and start the Java profiler if MOZ_PROFILER_STARTUP env var is specified
+     * Check and start the Java profiler if MOZ_PROFILER_STARTUP env var is specified.
      **/
-    protected void earlyStartJavaSampler(Intent intent)
-    {
-        String env = intent.getStringExtra("env0");
+    protected static void earlyStartJavaSampler(Intent intent) {
+        String env = StringUtils.getStringExtra(intent, "env0");
         for (int i = 1; env != null; i++) {
             if (env.startsWith("MOZ_PROFILER_STARTUP=")) {
                 if (!env.endsWith("=")) {
                     GeckoJavaSampler.start(10, 1000);
                     Log.d(LOGTAG, "Profiling Java on startup");
                 }
                 break;
             }
-            env = intent.getStringExtra("env" + i);
+            env = StringUtils.getStringExtra(intent, "env" + i);
         }
     }
 
     /**
      * Called when the activity is first created.
      *
      * Here we initialize all of our profile settings, Firefox Health Report,
      * and other one-shot constructions.
@@ -1117,17 +1117,17 @@ public abstract class GeckoApp
             enableStrictMode();
         }
 
         // The clock starts...now. Better hurry!
         mJavaUiStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_JAVAUI");
         mGeckoReadyStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_GECKOREADY");
 
         final Intent intent = getIntent();
-        final String args = intent.getStringExtra("args");
+        final String args = StringUtils.getStringExtra(intent, "args");
 
         earlyStartJavaSampler(intent);
 
         // GeckoLoader wants to dig some environment variables out of the
         // incoming intent, so pass it in here. GeckoLoader will do its
         // business later and dispose of the reference.
         GeckoLoader.setLastIntent(intent);
 
@@ -1481,17 +1481,17 @@ public abstract class GeckoApp
         // the last tabs section.
         if (!mShouldRestore) {
             getProfile().moveSessionFile();
         }
 
         Telemetry.HistogramAdd("FENNEC_STARTUP_GECKOAPP_ACTION", startupAction.ordinal());
 
         if (!mIsRestoringActivity) {
-            GeckoThread.setArgs(intent.getStringExtra("args"));
+            GeckoThread.setArgs(StringUtils.getStringExtra(intent, "args"));
             GeckoThread.setAction(intent.getAction());
             GeckoThread.setUri(passedUri);
         }
         if (!ACTION_DEBUG.equals(action) &&
             GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) {
             GeckoThread.createAndStart();
         } else if (ACTION_DEBUG.equals(action) &&
             GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.WaitForDebugger)) {
@@ -1891,30 +1891,32 @@ public abstract class GeckoApp
             Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class);
             // Copy extras.
             settingsIntent.putExtras(intent);
             startActivity(settingsIntent);
         }
     }
 
     /*
-     * Handles getting a uri from and intent in a way that is backwards
-     * compatable with our previous implementations
+     * Handles getting a URI from an intent in a way that is backwards-
+     * compatible with our previous implementations.
      */
     protected String getURIFromIntent(Intent intent) {
         final String action = intent.getAction();
-        if (ACTION_ALERT_CALLBACK.equals(action) || NotificationHelper.HELPER_BROADCAST_ACTION.equals(action))
+        if (ACTION_ALERT_CALLBACK.equals(action) || NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
             return null;
+        }
 
         String uri = intent.getDataString();
-        if (uri != null)
+        if (uri != null) {
             return uri;
+        }
 
         if ((action != null && action.startsWith(ACTION_WEBAPP_PREFIX)) || ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
-            uri = intent.getStringExtra("args");
+            uri = StringUtils.getStringExtra(intent, "args");
             if (uri != null && uri.startsWith("--url=")) {
                 uri.replace("--url=", "");
             }
         }
         return uri;
     }
 
     protected int getOrientation() {
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -41,16 +41,17 @@ import org.mozilla.gecko.mozglue.GeckoLo
 import org.mozilla.gecko.mozglue.JNITarget;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.mozglue.generatorannotations.OptionalGeneratedParameter;
 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
 import org.mozilla.gecko.prompts.PromptService;
 import org.mozilla.gecko.util.HardwareUtils;
 import org.mozilla.gecko.util.NativeJSContainer;
 import org.mozilla.gecko.util.ProxySelector;
+import org.mozilla.gecko.util.StringUtils;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.webapp.Allocator;
 
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -2570,20 +2571,25 @@ public class GeckoAppShell
                 return "PROXY " + proxy.address().toString();
             case SOCKS:
                 return "SOCKS " + proxy.address().toString();
         }
 
         return "DIRECT";
     }
 
-    /* Downloads the uri pointed to by a share intent, and alters the intent to point to the locally stored file.
+    /* Downloads the URI pointed to by a share intent, and alters the intent to point to the locally stored file.
      */
     public static void downloadImageForIntent(final Intent intent) {
-        final String src = intent.getStringExtra(Intent.EXTRA_TEXT);
+        final String src = StringUtils.getStringExtra(intent, Intent.EXTRA_TEXT);
+        if (src == null) {
+            showImageShareFailureToast();
+            return;
+        }
+
         final File dir = GeckoApp.getTempDirectory();
 
         if (dir == null) {
             showImageShareFailureToast();
             return;
         }
 
         GeckoApp.deleteTempFiles();
--- a/mobile/android/base/NotificationHelper.java
+++ b/mobile/android/base/NotificationHelper.java
@@ -2,17 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
-
+import org.mozilla.gecko.util.StringUtils;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.IntentFilter;
@@ -130,19 +130,19 @@ public final class NotificationHelper im
             // i.e. Don't bug the user if they're just closing a bunch of notifications.
             if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
                 return;
             }
         }
 
         JSONObject args = new JSONObject();
 
-        // The handler and cookie parameters are optional
+        // The handler and cookie parameters are optional.
         final String handler = data.getQueryParameter(HANDLER_ATTR);
-        final String cookie = i.getStringExtra(COOKIE_ATTR);
+        final String cookie = StringUtils.getStringExtra(i, COOKIE_ATTR);
 
         try {
             args.put(ID_ATTR, id);
             args.put(EVENT_TYPE_ATTR, notificationType);
             args.put(HANDLER_ATTR, handler);
             args.put(COOKIE_ATTR, cookie);
 
             if (BUTTON_EVENT.equals(notificationType)) {
--- a/mobile/android/base/util/StringUtils.java
+++ b/mobile/android/base/util/StringUtils.java
@@ -1,17 +1,19 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.util;
 
+import android.content.Intent;
 import android.net.Uri;
 import android.text.TextUtils;
+import android.util.Log;
 
 public class StringUtils {
 
     private static final String FILTER_URL_PREFIX = "filter://";
     private static final String USER_ENTERED_URL_PREFIX = "user-entered:";
 
     /*
      * This method tries to guess if the given string could be a search query or URL,
@@ -181,9 +183,18 @@ public class StringUtils {
             return uri.getSchemeSpecificPart();
         }
         return url;
     }
 
     public static String encodeUserEnteredUrl(String url) {
         return Uri.fromParts("user-entered", url, null).toString();
     }
+
+    public static String getStringExtra(Intent intent, String name) {
+        try {
+            return intent.getStringExtra(name);
+        } catch (android.os.BadParcelableException ex) {
+            Log.w("GeckoUtils", "Couldn't get string extra: malformed intent.");
+            return null;
+        }
+    }
 }