Skip to content

Commit

Permalink
Extract all libraries from ApkSoSource if at least one is corrupted
Browse files Browse the repository at this point in the history
Summary: Extract all libraries from /data/app if at least one of them is corrupted. So far, we would only unpack a subset of libraries, however that might cause issues/multiple recoveries when trying to load multiple libraries from /data/app that have a corrupted dependency.

Reviewed By: danjin250

Differential Revision: D48070086

fbshipit-source-id: 1058a8c3dcd52b5208960f7496a26621bbee33c1
  • Loading branch information
adicatana authored and facebook-github-bot committed Aug 17, 2023
1 parent 1323659 commit 6ee9ea6
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 55 deletions.
37 changes: 31 additions & 6 deletions java/com/facebook/soloader/ApkSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,40 @@ protected class ApkUnpacker extends ZipUnpacker {
}

@Override
protected boolean shouldExtract(ZipEntry ze, String soName) {
protected ZipDso[] getExtractableDsosFromZip() {
if (mDsos != null) {
return mDsos;
}

ZipDso[] dsos = computeDsosFromZip();
for (ZipDso zd : dsos) {
if (shouldExtract(zd.backingEntry, zd.name)) {
// If one library is corrupted, extract all of them to simplify the logic of computing
// dependencies. By default, the application so source (/data/app) relies on the bionic
// linker to resolve dependencies.
// If there's 2 /data/app libraries with the same corrupted depdendency, we might end up
// facing multiple load failures that can be avoided:
// A depends on C
// B depends on C
// C is corrupted
// Try to load A (from app so source - data/app) -> load C first (from /data/app) -> C
// fails, hence unpack A and C to /data/data
// Try to load B (from app so source - data/app) -> load C first (from /data/app) ->
// fail to load, even though C has previously been unpacked to /data/data and can be used
mDsos = dsos;
return mDsos;
}
}
mDsos = new ZipDso[0];
return mDsos;
}

private boolean shouldExtract(ZipEntry ze, String soName) {
StringBuilder msg = new StringBuilder();
boolean shouldExtract = false;
String zipPath = ze.getName();
if (soName.equals(mCorruptedLib)) {
mCorruptedLib = null;
msg.append("allowing consideration of corrupted lib ").append(soName);
shouldExtract = true;
} else if ((mFlags & PREFER_ANDROID_LIBS_DIRECTORY) == 0) {

if ((mFlags & PREFER_ANDROID_LIBS_DIRECTORY) == 0) {
msg.append("allowing consideration of ")
.append(zipPath)
.append(": self-extraction preferred");
Expand Down
57 changes: 17 additions & 40 deletions java/com/facebook/soloader/ExtractFromZipSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected Unpacker makeUnpacker() throws IOException {

protected class ZipUnpacker extends Unpacker {

private @Nullable ZipDso[] mDsos;
protected @Nullable ZipDso[] mDsos;
private final ZipFile mZipFile;
private final UnpackingSoSource mSoSource;

Expand All @@ -70,16 +70,7 @@ protected class ZipUnpacker extends Unpacker {
mSoSource = soSource;
}

/**
* If the list of zip DSOs is not created, generate that by iterating through all zip entries
* and doing pattern matching against a zipped libs pattern.
*
* @return mDsos
*/
final ZipDso[] ensureDsosInitialised() {
if (mDsos != null) {
return mDsos;
}
ZipDso[] computeDsosFromZip() {
LinkedHashSet<String> librariesAbiSet = new LinkedHashSet<>();
HashMap<String, ZipDso> providedLibraries = new HashMap<>();
Pattern zipSearchPattern = Pattern.compile(mZipSearchPattern);
Expand Down Expand Up @@ -108,36 +99,22 @@ final ZipDso[] ensureDsosInitialised() {

ZipDso[] dsos = providedLibraries.values().toArray(new ZipDso[providedLibraries.size()]);
Arrays.sort(dsos);
int nrFilteredDsos = 0;
for (int i = 0; i < dsos.length; ++i) {
ZipDso zd = dsos[i];
if (shouldExtract(zd.backingEntry, zd.name)) {
nrFilteredDsos += 1;
} else {
dsos[i] = null;
}
}
ZipDso[] filteredDsos = new ZipDso[nrFilteredDsos];
for (int i = 0, j = 0; i < dsos.length; ++i) {
ZipDso zd = dsos[i];
if (zd == null) {
continue;
}
filteredDsos[j++] = zd;
}
mDsos = filteredDsos;
return mDsos;
return dsos;
}

/**
* Hook for subclasses to filter out certain library names from being extracted from the zip
* file.
* If the list of zip DSOs is not created, generate that by iterating through all zip entries
* and doing pattern matching against a zipped libs pattern.
*
* @param soName Candidate soName
* @param ze Zip entry for file to extract
* @return mDsos
*/
protected boolean shouldExtract(ZipEntry ze, String soName) {
return true;
ZipDso[] getExtractableDsosFromZip() {
if (mDsos != null) {
return mDsos;
}

mDsos = computeDsosFromZip();
return mDsos;
}

@Override
Expand All @@ -147,7 +124,7 @@ public void close() throws IOException {

@Override
public final Dso[] getDsos() throws IOException {
return ensureDsosInitialised();
return getExtractableDsosFromZip();
}

@Override
Expand All @@ -161,13 +138,13 @@ private final class ZipBackedInputDsoIterator extends InputDsoIterator {

@Override
public boolean hasNext() {
ensureDsosInitialised();
getExtractableDsosFromZip();
return mCurrentDso < mDsos.length;
}

@Override
public InputDso next() throws IOException {
ensureDsosInitialised();
getExtractableDsosFromZip();
ZipDso zipDso = mDsos[mCurrentDso++];
InputStream is = mZipFile.getInputStream(zipDso.backingEntry);
try {
Expand Down Expand Up @@ -199,7 +176,7 @@ protected String computeFileHash(File file) {
}
}

private static final class ZipDso extends Dso implements Comparable<ZipDso> {
protected static final class ZipDso extends Dso implements Comparable<ZipDso> {

final ZipEntry backingEntry;
final int abiScore;
Expand Down
7 changes: 0 additions & 7 deletions java/com/facebook/soloader/UnpackingSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ public abstract class UnpackingSoSource extends DirectorySoSource implements Asy
private static final byte STATE_CLEAN = 1;

protected final Context mContext;
@Nullable protected String mCorruptedLib;

@Nullable private String[] mAbis;

Expand Down Expand Up @@ -538,12 +537,6 @@ public String getLibraryPath(String soName) throws IOException {
return soFile.getCanonicalPath();
}

/** Prepare this SoSource extracting a corrupted library. */
public void prepare(String soName) throws IOException {
mCorruptedLib = soName;
prepareForceRefresh();
}

/** Prepare this SoSource by force extracting a corrupted library. */
public void prepareForceRefresh() throws IOException {
prepare(SoSource.PREPARE_FLAG_FORCE_REFRESH);
Expand Down
4 changes: 2 additions & 2 deletions java/com/facebook/soloader/recovery/ReunpackSoSources.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public boolean recover(UnsatisfiedLinkError error, SoSource[] soSources) {
LogUtil.e(SoLoader.TAG, "Runpacking ApkSoSource " + uss.getClass().getName());
try {
// Re-unpack the ApkSoSource libraries first
uss.prepare(soName);
uss.prepareForceRefresh();
} catch (Exception e) {
// Catch a general error and log it, rather than failing during recovery and crashing the
// app
Expand All @@ -88,7 +88,7 @@ public boolean recover(UnsatisfiedLinkError error, SoSource[] soSources) {
LogUtil.e(SoLoader.TAG, "Runpacking " + uss.getClass().getName());
try {
// Re-unpack from other UnpackingSoSources as well
uss.prepare(soName);
uss.prepareForceRefresh();
} catch (Exception e) {
// Catch a general error and log it, rather than failing during recovery and crashing the
// app
Expand Down

0 comments on commit 6ee9ea6

Please sign in to comment.