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

Commit 1d3c4b39 authored by Chet Haase's avatar Chet Haase
Browse files

Fix infinite recursion in hashcode of Spannables

An app created a SpannableStringBuilder, one of which's spans was the
instance of the string builder itself (that is, the builder contained a span
that was the builder). This caused infinite recursion in the hashcode()
method because it computes a hash from its fields, including all of its spans.

The fix detects the case where a span equals the current instance and
noops the computation on that span. A similar adjustment was made to equals()
to avoid the same recursion problem.

Issue #11051658 StackOverflowError in android.text.SpannableStringBuilder.hashCode

Change-Id: I742687ab32d81ac51c4b9135f698cf5e96a1d295
parent 76a5c86d
Loading
Loading
Loading
Loading
+16 −8
Original line number Diff line number Diff line
@@ -1293,23 +1293,29 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
    public boolean equals(Object o) {
        if (o instanceof Spanned &&
                toString().equals(o.toString())) {
            Spanned other = (Spanned) o;
            // Check span data
            Object[] otherSpans = ((Spanned) o).getSpans(0,
                    ((Spanned) o).length(), Object.class);
            Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
            if (mSpanCount == otherSpans.length) {
                for (int i = 0; i < mSpanCount; ++i) {
                    Object thisSpan = mSpans[i];
                    Object otherSpan = otherSpans[i];
                    if (!thisSpan.equals(otherSpan) ||
                            getSpanStart(thisSpan) != getSpanStart(otherSpan) ||
                            getSpanEnd(thisSpan) != getSpanEnd(otherSpan) ||
                            getSpanFlags(thisSpan) != getSpanFlags(otherSpan)) {
                    if (thisSpan == this) {
                        if (other != otherSpan ||
                                getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
                                getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
                                getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
                            return false;
                        }
                    } else if (!thisSpan.equals(otherSpan) ||
                            getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
                            getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
                            getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
                        return false;
                    }
                }
                return true;
            }

        }
        return false;
    }
@@ -1321,7 +1327,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
        hash = hash * 31 + mSpanCount;
        for (int i = 0; i < mSpanCount; ++i) {
            Object span = mSpans[i];
            if (span != this) {
                hash = hash * 31 + span.hashCode();
            }
            hash = hash * 31 + getSpanStart(span);
            hash = hash * 31 + getSpanEnd(span);
            hash = hash * 31 + getSpanFlags(span);
+16 −8
Original line number Diff line number Diff line
@@ -363,23 +363,29 @@ import java.lang.reflect.Array;
    public boolean equals(Object o) {
        if (o instanceof Spanned &&
                toString().equals(o.toString())) {
            Spanned other = (Spanned) o;
            // Check span data
            Object[] otherSpans = ((Spanned) o).getSpans(0,
                    ((Spanned) o).length(), Object.class);
            Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
            if (mSpanCount == otherSpans.length) {
                for (int i = 0; i < mSpanCount; ++i) {
                    Object thisSpan = mSpans[i];
                    Object otherSpan = otherSpans[i];
                    if (!thisSpan.equals(otherSpan) ||
                            getSpanStart(thisSpan) != getSpanStart(otherSpan) ||
                            getSpanEnd(thisSpan) != getSpanEnd(otherSpan) ||
                            getSpanFlags(thisSpan) != getSpanFlags(otherSpan)) {
                    if (thisSpan == this) {
                        if (other != otherSpan ||
                                getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
                                getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
                                getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
                            return false;
                        }
                    } else if (!thisSpan.equals(otherSpan) ||
                            getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
                            getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
                            getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
                        return false;
                    }
                }
                return true;
            }

        }
        return false;
    }
@@ -391,7 +397,9 @@ import java.lang.reflect.Array;
        hash = hash * 31 + mSpanCount;
        for (int i = 0; i < mSpanCount; ++i) {
            Object span = mSpans[i];
            if (span != this) {
                hash = hash * 31 + span.hashCode();
            }
            hash = hash * 31 + getSpanStart(span);
            hash = hash * 31 + getSpanEnd(span);
            hash = hash * 31 + getSpanFlags(span);