Skip to content

Commit 6f692e2

Browse files
schfan-1OhMyVenyx
authored andcommitted
[RESTRICT AUTOMERGE] [SettingsProvider] mem limit should be checked before settings are updated
Previously, a setting is updated before the memory usage limit check, which can be exploited by malicious apps and cause OoM DoS. This CL changes the logic to checkMemLimit -> update -> updateMemUsage. BUG: 239415861 Test: atest com.android.providers.settings.SettingsStateTest (cherry picked from commit 8eeb929) Merged-In: I20551a2dba9aa79efa0c064824f349f551c2c2e4 Change-Id: I20551a2dba9aa79efa0c064824f349f551c2c2e4 (cherry picked from commit 966b597) Merged-In: I20551a2dba9aa79efa0c064824f349f551c2c2e4
1 parent 847c626 commit 6f692e2

2 files changed

Lines changed: 86 additions & 27 deletions

File tree

packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,11 @@ public void resetSettingDefaultValueLocked(String name) {
375375
Setting newSetting = new Setting(name, oldSetting.getValue(), null,
376376
oldSetting.getPackageName(), oldSetting.getTag(), false,
377377
oldSetting.getId());
378-
mSettings.put(name, newSetting);
379-
updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
378+
int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
380379
newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
380+
checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
381+
mSettings.put(name, newSetting);
382+
updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
381383
scheduleWriteIfNeededLocked();
382384
}
383385
}
@@ -410,6 +412,12 @@ public boolean insertSettingLocked(String name, String value, String tag,
410412
Setting oldState = mSettings.get(name);
411413
String oldValue = (oldState != null) ? oldState.value : null;
412414
String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
415+
String newDefaultValue = makeDefault ? value : oldDefaultValue;
416+
417+
int newSize = getNewMemoryUsagePerPackageLocked(packageName, oldValue, value,
418+
oldDefaultValue, newDefaultValue);
419+
checkNewMemoryUsagePerPackageLocked(packageName, newSize);
420+
413421
Setting newState;
414422

415423
if (oldState != null) {
@@ -430,8 +438,7 @@ oldValue, tag, makeDefault, getUserIdFromKey(mKey),
430438

431439
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
432440

433-
updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
434-
oldDefaultValue, newState.getDefaultValue());
441+
updateMemoryUsagePerPackageLocked(packageName, newSize);
435442

436443
scheduleWriteIfNeededLocked();
437444

@@ -552,13 +559,14 @@ public boolean deleteSettingLocked(String name) {
552559
}
553560

554561
Setting oldState = mSettings.remove(name);
562+
int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
563+
null, oldState.defaultValue, null);
555564

556565
FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
557566
/* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
558567
FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
559568

560-
updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
561-
null, oldState.defaultValue, null);
569+
updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
562570

563571
addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
564572

@@ -579,16 +587,18 @@ public boolean resetSettingLocked(String name) {
579587
Setting oldSetting = new Setting(setting);
580588
String oldValue = setting.getValue();
581589
String oldDefaultValue = setting.getDefaultValue();
590+
String newValue = oldDefaultValue;
591+
String newDefaultValue = oldDefaultValue;
592+
593+
int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, oldValue,
594+
newValue, oldDefaultValue, newDefaultValue);
595+
checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
582596

583597
if (!setting.reset()) {
584598
return false;
585599
}
586600

587-
String newValue = setting.getValue();
588-
String newDefaultValue = setting.getDefaultValue();
589-
590-
updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
591-
newValue, oldDefaultValue, newDefaultValue);
601+
updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
592602

593603
addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
594604

@@ -696,38 +706,49 @@ public void dumpHistoricalOperations(PrintWriter pw) {
696706
}
697707

698708
@GuardedBy("mLock")
699-
private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
700-
String newValue, String oldDefaultValue, String newDefaultValue) {
701-
if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
702-
return;
703-
}
709+
private boolean isExemptFromMemoryUsageCap(String packageName) {
710+
return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
711+
|| SYSTEM_PACKAGE_NAME.equals(packageName);
712+
}
704713

705-
if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
714+
@GuardedBy("mLock")
715+
private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
716+
throws IllegalStateException {
717+
if (isExemptFromMemoryUsageCap(packageName)) {
706718
return;
707719
}
720+
if (newSize > mMaxBytesPerAppPackage) {
721+
throw new IllegalStateException("You are adding too many system settings. "
722+
+ "You should stop using system settings for app specific data"
723+
+ " package: " + packageName);
724+
}
725+
}
708726

727+
@GuardedBy("mLock")
728+
private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValue,
729+
String newValue, String oldDefaultValue, String newDefaultValue) {
730+
if (isExemptFromMemoryUsageCap(packageName)) {
731+
return 0;
732+
}
733+
final Integer currentSize = mPackageToMemoryUsage.get(packageName);
709734
final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
710735
final int newValueSize = (newValue != null) ? newValue.length() : 0;
711736
final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
712737
final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
713738
final int deltaSize = newValueSize + newDefaultValueSize
714739
- oldValueSize - oldDefaultValueSize;
740+
return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
741+
}
715742

716-
Integer currentSize = mPackageToMemoryUsage.get(packageName);
717-
final int newSize = Math.max((currentSize != null)
718-
? currentSize + deltaSize : deltaSize, 0);
719-
720-
if (newSize > mMaxBytesPerAppPackage) {
721-
throw new IllegalStateException("You are adding too many system settings. "
722-
+ "You should stop using system settings for app specific data"
723-
+ " package: " + packageName);
743+
@GuardedBy("mLock")
744+
private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
745+
if (isExemptFromMemoryUsageCap(packageName)) {
746+
return;
724747
}
725-
726748
if (DEBUG) {
727749
Slog.i(LOG_TAG, "Settings for package: " + packageName
728750
+ " size: " + newSize + " bytes.");
729751
}
730-
731752
mPackageToMemoryUsage.put(packageName, newSize);
732753
}
733754

packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import android.util.TypedXmlSerializer;
2121
import android.util.Xml;
2222

23+
import com.google.common.base.Strings;
24+
2325
import java.io.ByteArrayOutputStream;
2426
import java.io.File;
2527
import java.io.FileOutputStream;
@@ -276,4 +278,40 @@ private SettingsState getSettingStateObject() {
276278
settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
277279
return settingsState;
278280
}
281+
282+
public void testInsertSetting_memoryUsage() {
283+
SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
284+
SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
285+
// No exception should be thrown when there is no cap
286+
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
287+
null, false, "p1");
288+
settingsState.deleteSettingLocked(SETTING_NAME);
289+
290+
settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
291+
SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
292+
// System package doesn't have memory usage limit
293+
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
294+
null, false, SYSTEM_PACKAGE);
295+
settingsState.deleteSettingLocked(SETTING_NAME);
296+
297+
// Should not throw if usage is under the cap
298+
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19999),
299+
null, false, "p1");
300+
settingsState.deleteSettingLocked(SETTING_NAME);
301+
try {
302+
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
303+
null, false, "p1");
304+
fail("Should throw because it exceeded per package memory usage");
305+
} catch (IllegalStateException ex) {
306+
assertTrue(ex.getMessage().contains("p1"));
307+
}
308+
try {
309+
settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
310+
null, false, "p1");
311+
fail("Should throw because it exceeded per package memory usage");
312+
} catch (IllegalStateException ex) {
313+
assertTrue(ex.getMessage().contains("p1"));
314+
}
315+
assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
316+
}
279317
}

0 commit comments

Comments
 (0)