Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 841594d2 authored by Andrew Cheng's avatar Andrew Cheng Committed by Andrew Cheng
Browse files

NPE from bMessage MIME parsing crashes BT

When a Pixel phone receives a MMS that contains only an image and no
text, it will replace the image data with a text string (e.g., filename)
in the bMessage (if the MAP client specified "no attachments" option).
However, the Pixel does not set the charset encoding to UTF-8 in this
case.

When the MAP client receives a bMessage that does not specify a charset
encoding, it will set the message body to NULL, leading to a NPE that
crashes the BT stack when the MAP client tries to parse a NULL string.

This CL introduces fixes at two points, each of which in isolation
prevents the NPE and BT from crashing. First, instead of setting the
message body to NULL when the UTF-8 check fails, it is set to a
non-UTF-8 encoded string, since the previous assumption that MMS will
always be text-only is no longer valid. The end effect from this change
is the user now sees the replacement text string, e.g., filename.

Second, as an additional failsafe against BT crashing, a NULL-check is
added before any string parsing begins. The end effect from this change
is the user will see a message with an empty-string body. Given the
first fix, this second fix should not be triggered.

Tag: #stability

Bug: 144759976
Test: atest BluetoothMapbMessageMimeTest
Test: Send an image-only MMS to the remote device. A notification on the
carkit should appear indicating a message has been received, where the
image has been replaced with a text string.

Change-Id: I9c2907d6fc42594382cc1eebdf0bcd49cdc44cac
parent aa629f8c
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -744,6 +744,12 @@ public class BluetoothMapbMessageMime extends BluetoothMapbMessage {
    }
    }


    private void parseMime(String message) {
    private void parseMime(String message) {
        // Check for null String, otherwise NPE will cause BT to crash
        if (message == null) {
            Log.e(TAG, "parseMime called with a NULL message, terminating early");
            return;
        }

        /* Overall strategy for decoding:
        /* Overall strategy for decoding:
         * 1) split on first empty line to extract the header
         * 1) split on first empty line to extract the header
         * 2) unfold and parse headers
         * 2) unfold and parse headers
@@ -755,6 +761,7 @@ public class BluetoothMapbMessageMime extends BluetoothMapbMessage {
        String[] mimeParts;
        String[] mimeParts;
        String remaining = null;
        String remaining = null;
        String messageBody = null;
        String messageBody = null;

        message = message.replaceAll("\\r\\n[ \\\t]+", ""); // Unfold
        message = message.replaceAll("\\r\\n[ \\\t]+", ""); // Unfold
        messageParts = message.split("\r\n\r\n", 2); // Split the header from the body
        messageParts = message.split("\r\n\r\n", 2); // Split the header from the body
        if (messageParts.length != 2) {
        if (messageParts.length != 2) {
+5 −2
Original line number Original line Diff line number Diff line
@@ -289,9 +289,12 @@ class BmessageParser {
         * non-text content. If the charset is not set to UTF-8, it is safe to set the message as
         * non-text content. If the charset is not set to UTF-8, it is safe to set the message as
         * empty. We force the getMessage (see Client) to only call getMessage with
         * empty. We force the getMessage (see Client) to only call getMessage with
         * UTF-8 as the MCE is not obliged to support native charset.
         * UTF-8 as the MCE is not obliged to support native charset.
         *
         * 2020-06-01: we could now expect MMS to be more than text, e.g., image-only, so charset
         * not always UTF-8, downgrading log message from ERROR to DEBUG.
         */
         */
        if (!"UTF-8".equals(mBmsg.mBbodyCharset)) {
        if (!"UTF-8".equals(mBmsg.mBbodyCharset)) {
            Log.e(TAG, "The charset was not set to charset UTF-8: " + mBmsg.mBbodyCharset);
            Log.d(TAG, "The charset was not set to charset UTF-8: " + mBmsg.mBbodyCharset);
        }
        }


        /*
        /*
@@ -325,7 +328,7 @@ class BmessageParser {
                if ("UTF-8".equals(mBmsg.mBbodyCharset)) {
                if ("UTF-8".equals(mBmsg.mBbodyCharset)) {
                    mBmsg.mMessage = new String(data, 0, messageLen, StandardCharsets.UTF_8);
                    mBmsg.mMessage = new String(data, 0, messageLen, StandardCharsets.UTF_8);
                } else {
                } else {
                    mBmsg.mMessage = null;
                    mBmsg.mMessage = new String(data, 0, messageLen);
                }
                }
            } else {
            } else {
                /* Handle possible exception for incorrect LENGTH value
                /* Handle possible exception for incorrect LENGTH value
+37 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.bluetooth.map;

import static org.mockito.Mockito.*;

import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@MediumTest
@RunWith(AndroidJUnit4.class)
public class BluetoothMapbMessageMimeTest {
    private static final String TAG = BluetoothMapbMessageMimeTest.class.getSimpleName();

    @Test
    public void testParseNullMsgPart_NoExceptionsThrown() {
        BluetoothMapbMessageMime bMessageMime = new BluetoothMapbMessageMime();
        bMessageMime.parseMsgPart(null);
    }
}