diff options
author | Chet Haase <chet@google.com> | 2013-10-03 17:21:14 -0700 |
---|---|---|
committer | Chet Haase <chet@google.com> | 2013-10-04 07:24:19 -0700 |
commit | 1d3c4b396a393515e738cb72782ef564362420fa (patch) | |
tree | 73e700050a2f9f91fd7bf57c574af770c1ce4286 | |
parent | 76a5c86df7902870c87ff07ffd0628cb10f32c00 (diff) | |
download | frameworks_base-1d3c4b396a393515e738cb72782ef564362420fa.zip frameworks_base-1d3c4b396a393515e738cb72782ef564362420fa.tar.gz frameworks_base-1d3c4b396a393515e738cb72782ef564362420fa.tar.bz2 |
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
-rw-r--r-- | core/java/android/text/SpannableStringBuilder.java | 24 | ||||
-rw-r--r-- | core/java/android/text/SpannableStringInternal.java | 24 |
2 files changed, 32 insertions, 16 deletions
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 6efde05..34274a6 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -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]; - hash = hash * 31 + span.hashCode(); + if (span != this) { + hash = hash * 31 + span.hashCode(); + } hash = hash * 31 + getSpanStart(span); hash = hash * 31 + getSpanEnd(span); hash = hash * 31 + getSpanFlags(span); diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 9b24a2e..456a3e5 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -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]; - hash = hash * 31 + span.hashCode(); + if (span != this) { + hash = hash * 31 + span.hashCode(); + } hash = hash * 31 + getSpanStart(span); hash = hash * 31 + getSpanEnd(span); hash = hash * 31 + getSpanFlags(span); |