Rejigger the invalid-key checks at backup time

Bug 13732002

Change-Id: Ic8f71234d1bbc7420eaa8e1762b999d09f308d46
This commit is contained in:
Christopher Tate
2014-04-01 10:38:29 -07:00
parent 4bb047fa5e
commit cba5941c60
4 changed files with 81 additions and 14 deletions

View File

@ -124,4 +124,12 @@ oneway interface IBackupAgent {
int type, String domain, String path, long mode, long mtime,
int token, IBackupManager callbackBinder);
/**
* Out of band: instruct the agent to crash within the client process. This is used
* when the backup infrastructure detects a semantic error post-hoc and needs to
* pass the problem back to the app.
*
* @param message The message to be passed to the agent's application in an exception.
*/
void fail(String message);
}

View File

@ -128,6 +128,13 @@ public abstract class BackupAgent extends ContextWrapper {
Handler mHandler = null;
Handler getHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
}
return mHandler;
}
class SharedPrefsSynchronizer implements Runnable {
public final CountDownLatch mLatch = new CountDownLatch(1);
@ -140,12 +147,9 @@ public abstract class BackupAgent extends ContextWrapper {
// Syncing shared preferences deferred writes needs to happen on the main looper thread
private void waitForSharedPrefs() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
}
Handler h = getHandler();
final SharedPrefsSynchronizer s = new SharedPrefsSynchronizer();
mHandler.postAtFrontOfQueue(s);
h.postAtFrontOfQueue(s);
try {
s.mLatch.await();
} catch (InterruptedException e) { /* ignored */ }
@ -680,5 +684,23 @@ public abstract class BackupAgent extends ContextWrapper {
}
}
}
@Override
public void fail(String message) {
getHandler().post(new FailRunnable(message));
}
}
static class FailRunnable implements Runnable {
private String mMessage;
FailRunnable(String message) {
mMessage = message;
}
@Override
public void run() {
throw new IllegalStateException(mMessage);
}
}
}

View File

@ -85,11 +85,6 @@ public class BackupDataOutput {
* @throws IOException if the write failed
*/
public int writeEntityHeader(String key, int dataSize) throws IOException {
if (key != null && key.charAt(0) >= 0xff00) {
if (Process.myUid() != Process.SYSTEM_UID) {
throw new IllegalArgumentException("Invalid key " + key);
}
}
int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
if (result >= 0) {
return result;

View File

@ -2035,6 +2035,7 @@ public class BackupManagerService extends IBackupManager.Stub {
BackupState mCurrentState;
// carried information about the current in-flight operation
IBackupAgent mAgentBinder;
PackageInfo mCurrentPackage;
File mSavedStateName;
File mBackupDataName;
@ -2097,6 +2098,7 @@ public class BackupManagerService extends IBackupManager.Stub {
addBackupTrace(b.toString());
}
mAgentBinder = null;
mStatus = BackupConstants.TRANSPORT_OK;
// Sanity check: if the queue is empty we have no work to do.
@ -2228,6 +2230,7 @@ public class BackupManagerService extends IBackupManager.Stub {
IApplicationThread.BACKUP_MODE_INCREMENTAL);
addBackupTrace("agent bound; a? = " + (agent != null));
if (agent != null) {
mAgentBinder = agent;
mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
// at this point we'll either get a completion callback from the
// agent, or a timeout message on the main handler. either way, we're
@ -2253,6 +2256,7 @@ public class BackupManagerService extends IBackupManager.Stub {
// That means we need to direct to the next state ourselves.
if (mStatus != BackupConstants.TRANSPORT_OK) {
BackupState nextState = BackupState.RUNNING_QUEUE;
mAgentBinder = null;
// An agent-level failure means we reenqueue this one agent for
// a later retry, but otherwise proceed normally.
@ -2274,6 +2278,7 @@ public class BackupManagerService extends IBackupManager.Stub {
executeNextState(nextState);
} else {
// success case
addBackupTrace("expecting completion/timeout callback");
}
}
@ -2402,14 +2407,52 @@ public class BackupManagerService extends IBackupManager.Stub {
return BackupConstants.TRANSPORT_OK;
}
public void failAgent(IBackupAgent agent, String message) {
try {
agent.fail(message);
} catch (Exception e) {
Slog.w(TAG, "Error conveying failure to " + mCurrentPackage.packageName);
}
}
@Override
public void operationComplete() {
// Okay, the agent successfully reported back to us. The next thing we do is
// push the app widget state for the app, if any.
// Okay, the agent successfully reported back to us!
final String pkgName = mCurrentPackage.packageName;
final long filepos = mBackupDataName.length();
FileDescriptor fd = mBackupData.getFileDescriptor();
try {
// If it's a 3rd party app, see whether they wrote any protected keys
// and complain mightily if they are attempting shenanigans.
if (mCurrentPackage.applicationInfo != null &&
(mCurrentPackage.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
ParcelFileDescriptor readFd = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
BackupDataInput in = new BackupDataInput(readFd.getFileDescriptor());
try {
while (in.readNextHeader()) {
final String key = in.getKey();
if (key != null && key.charAt(0) >= 0xff00) {
// Not okay: crash them and bail.
failAgent(mAgentBinder, "Illegal backup key: " + key);
addBackupTrace("illegal key " + key + " from " + pkgName);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, pkgName,
"bad key");
mBackupHandler.removeMessages(MSG_TIMEOUT);
agentErrorCleanup();
// agentErrorCleanup() implicitly executes next state properly
return;
}
in.skipEntityData();
}
} finally {
if (readFd != null) {
readFd.close();
}
}
}
// Piggyback the widget state payload, if any
BackupDataOutput out = new BackupDataOutput(fd);
byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
UserHandle.USER_OWNER);
@ -2434,8 +2477,7 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
// Spin the data off to the
// transport and proceed with the next stage.
// Spin the data off to the transport and proceed with the next stage.
if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
+ pkgName);
mBackupHandler.removeMessages(MSG_TIMEOUT);