mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-28 15:53:51 +01:00
Global: fixes several issues found by AI
This commit is contained in:
@@ -284,3 +284,26 @@ void ffBinderServiceHandleRelease(FFBinderServiceHandle* service) {
|
||||
ioctl(service->binder->fd, BINDER_WRITE_READ, &release);
|
||||
service->handle = 0;
|
||||
}
|
||||
|
||||
void ffBinderAcquiredRelease(FFBinderAcquired* acquired) {
|
||||
if (acquired->replied == nullptr || *acquired->replied == false) {
|
||||
return; // nothing was acquired
|
||||
}
|
||||
|
||||
// A descriptor is not a reference: the kernel installed it in our own fd table when it delivered
|
||||
// the reply, and nobody else holds it, so closing it is the whole of giving it back.
|
||||
for (uint32_t i = 0; i < acquired->fdCount; i++) {
|
||||
close(acquired->fds[i]);
|
||||
}
|
||||
|
||||
// The mirror of the acquire the transaction appended to its tail, in the same order it took them.
|
||||
// No binder to talk through means the handles went with it when it was closed.
|
||||
if (acquired->binder == nullptr || acquired->binder->fd < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < acquired->handleCount; i++) {
|
||||
FFBinderServiceHandle handle = { .binder = acquired->binder, .handle = acquired->handles[i] };
|
||||
ffBinderServiceHandleRelease(&handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
// * A handle arriving in a reply is owned by that reply buffer. It has to be acquired
|
||||
// (BC_ACQUIRE + BC_INCREFS) before BC_FREE_BUFFER; otherwise only a weak reference survives and
|
||||
// every later transaction on it fails, because the kernel looks up a strong reference.
|
||||
//
|
||||
// See .workbuddy-ai/android-binder-raw-client.md for the full write-up and the tooling.
|
||||
|
||||
#include "fastfetch.h" // IWYU pragma: keep
|
||||
|
||||
@@ -251,3 +249,29 @@ typedef struct FFBinderServiceHandle {
|
||||
// Returns the strong and weak references ffBinderLookupService() acquired on the handle. Usable as a
|
||||
// cleanup attribute; safe on a zeroed or already released FFBinderServiceHandle.
|
||||
[[gnu::nonnull(1)]] void ffBinderServiceHandleRelease(FFBinderServiceHandle* service);
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Handles a transaction acquired
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
// Whatever ffBinderTransact() collected for the caller, released together on the way out of a
|
||||
// function -- the handles it acquired, plus the descriptors it delivered, which are owned rather
|
||||
// than borrowed and would otherwise stay open for the life of the process.
|
||||
//
|
||||
// Unlike FFBinderServiceHandle this cannot be declared before the transaction runs, because which
|
||||
// handles came back is only known afterwards. `replied` is therefore a separate flag the caller
|
||||
// raises once ffBinderTransact() has returned: it acquires the handles even when it goes on to fail
|
||||
// (a reply that did not fit the caller buffer still had its BC_ACQUIRE tail sent), so releasing
|
||||
// without it would drop a reference that was never taken.
|
||||
typedef struct FFBinderAcquired {
|
||||
FFBinder* binder;
|
||||
const uint32_t* handles;
|
||||
uint32_t handleCount;
|
||||
const int32_t* fds;
|
||||
uint32_t fdCount;
|
||||
const bool* replied;
|
||||
} FFBinderAcquired;
|
||||
|
||||
// Usable as a cleanup attribute; safe on a zeroed FFBinderAcquired, and on one whose transaction did
|
||||
// not reply.
|
||||
[[gnu::nonnull(1)]] void ffBinderAcquiredRelease(FFBinderAcquired* acquired);
|
||||
|
||||
+56
-14
@@ -95,28 +95,60 @@ static uint32_t dexUleb128(const uint8_t** p, const uint8_t* end) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// encoded_value: one header byte holding ((size - 1) << 5) | type, then that many payload bytes. A
|
||||
// `static final int` constant is written as VALUE_INT, whose payload is a sign-extended
|
||||
// little-endian integer. Reading through 64 bits keeps every shift in range even if the size field
|
||||
// is nonsense; `end` is what keeps a truncated payload from running off the end of the mapping.
|
||||
static int32_t dexEncodedInt(const uint8_t** p, const uint8_t* end) {
|
||||
// The type tag a dex encoded_value carries in the low five bits of its header byte, per the
|
||||
// `encoded_value` table of the dex format (`VALUE_INT = 0x04`, sign-extended four-byte integer).
|
||||
// 0x1f is VALUE_BOOLEAN in that same table, which is the trap: naming it VALUE_INT and comparing
|
||||
// against it rejects every real int and accepts booleans.
|
||||
#define FF_DEX_VALUE_INT 0x04
|
||||
|
||||
// encoded_value: one header byte holding ((size - 1) << 5) | type, then that many payload bytes.
|
||||
//
|
||||
// The `static_values` array is not a list of ints: `IInterface.DESCRIPTOR` is a `static final
|
||||
// String` and sits in it, and a real Stub class mixes ints, strings and booleans. So a value whose
|
||||
// tag is not VALUE_INT is skipped -- its payload is stepped over to keep the walk aligned with the
|
||||
// field list -- and only `isInt` reports whether the *wanted* field ended up being one. Failing on
|
||||
// the first non-int instead would abort on `DESCRIPTOR`, before the transaction constants that
|
||||
// follow it are ever reached. Measured on a device: `IWifiManager$Stub` carries 347 static fields
|
||||
// whose values are mostly VALUE_INT but include that string, and `getConnectionInfo` is 98 there.
|
||||
//
|
||||
// A payload wider than four bytes is not an int either, and the byte count is what keeps a size
|
||||
// field read out of a corrupt file from shifting past the end. Reading through 64 bits keeps every
|
||||
// shift in range while counting down; `end` is what keeps a truncated payload from running off the
|
||||
// end of the mapping.
|
||||
static bool dexEncodedValue(const uint8_t** p, const uint8_t* end, int32_t* result, bool* isInt) {
|
||||
if (*p >= end) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
const uint8_t header = *(*p)++;
|
||||
const uint32_t type = (uint32_t) (header & 0x1f);
|
||||
const uint32_t size = (uint32_t) (header >> 5) + 1;
|
||||
if (size > 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t value = 0;
|
||||
for (uint32_t i = 0; i < size && i < 8; ++i) {
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
if (*p >= end) {
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
if (i < sizeof(value)) {
|
||||
value |= (uint64_t) (*(*p)++) << (i * 8);
|
||||
} else {
|
||||
(void) *(*p)++;
|
||||
}
|
||||
value |= (uint64_t) (*(*p)++) << (i * 8);
|
||||
}
|
||||
const uint32_t bits = size * 8;
|
||||
if (bits < 64 && (value & (1ull << (bits - 1))) != 0) {
|
||||
value |= ~((1ull << bits) - 1);
|
||||
|
||||
*isInt = type == FF_DEX_VALUE_INT && size <= sizeof(uint32_t);
|
||||
if (*isInt) {
|
||||
// VALUE_INT is sign-extended from its own width, not from 32 bits: a one-byte -1 is written
|
||||
// as `21 ff`, one byte of 0xff, and has to come back as -1 rather than 255.
|
||||
const uint32_t bits = size * 8;
|
||||
if ((value & (1ull << (bits - 1))) != 0) {
|
||||
value |= ~((1ull << bits) - 1);
|
||||
}
|
||||
}
|
||||
return (int32_t) (uint32_t) value;
|
||||
*result = (int32_t) (uint32_t) value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// string_data_item: a uleb128 length in UTF-16 code units, then MUTF-8 bytes and a NUL terminator.
|
||||
@@ -216,13 +248,23 @@ static const char* dexStaticInt(const uint8_t* dex, size_t size, const char* cla
|
||||
}
|
||||
fieldIndex += fieldIndexDiff;
|
||||
(void) dexUleb128(&fields, end); // access_flags
|
||||
const int32_t value = dexEncodedInt(&values, end);
|
||||
int32_t value;
|
||||
bool isInt;
|
||||
if (!dexEncodedValue(&values, end, &value, &isInt)) {
|
||||
return "The dex static value list is truncated";
|
||||
}
|
||||
|
||||
if (!dexTableInRange(dex, end, fieldIds, (uint64_t) fieldIndex + 1, FF_DEX_FIELD_ID_SIZE)) {
|
||||
return "The dex field table is out of range";
|
||||
}
|
||||
const char* name = dexString(dex, end, dexU32(fieldIds + (size_t) fieldIndex * FF_DEX_FIELD_ID_SIZE + FF_DEX_OFF_FIELD_ID_NAME));
|
||||
if (name != nullptr && strcmp(name, fieldName) == 0) {
|
||||
if (!isInt) {
|
||||
// The field exists but is not an int, so its value is not the constant asked
|
||||
// for. Reported here rather than on the first non-int in the list, because the
|
||||
// list legitimately holds strings (`DESCRIPTOR`) and booleans.
|
||||
return "The dex static field is not an int";
|
||||
}
|
||||
*result = value;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -362,8 +362,18 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
}
|
||||
|
||||
ffStrbufAppendC(folder, '*');
|
||||
WIN32_FIND_DATAA entry;
|
||||
HANDLE hFind = FindFirstFileA(folder->chars, &entry);
|
||||
// The wide API on purpose: every path fastfetch holds is UTF-8, and FindFirstFileA would read
|
||||
// it in the active code page, so a directory whose name the code page cannot represent -- a
|
||||
// Chinese or Japanese name under an English or German locale, for one -- answers
|
||||
// ERROR_PATH_NOT_FOUND and the whole listing disappears. The names it returns are converted
|
||||
// back to UTF-8 below.
|
||||
wchar_t wideFolder[PATH_MAX];
|
||||
if (!NT_SUCCESS(RtlUTF8ToUnicodeN(wideFolder, sizeof(wideFolder), nullptr, folder->chars, folder->length + 1))) {
|
||||
ffStrbufTrimRight(folder, '*');
|
||||
return;
|
||||
}
|
||||
WIN32_FIND_DATAW entry;
|
||||
HANDLE hFind = FindFirstFileW(wideFolder, &entry);
|
||||
ffStrbufTrimRight(folder, '*');
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
@@ -371,14 +381,15 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
|
||||
do {
|
||||
if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (ffStrEquals(entry.cFileName, ".") || ffStrEquals(entry.cFileName, "..")) {
|
||||
if (entry.cFileName[0] == L'.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBefore(folder, folderLength);
|
||||
ffStrbufAppendS(folder, entry.cFileName);
|
||||
ffStrbufAppendWS(folder, entry.cFileName);
|
||||
ffStrbufAppendC(folder, '/');
|
||||
listFilesRecursively(baseLength, folder, (uint8_t) (indentation + 1), entry.cFileName, pretty);
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(entry.cFileName);
|
||||
listFilesRecursively(baseLength, folder, (uint8_t) (indentation + 1), name.chars, pretty);
|
||||
ffStrbufSubstrBefore(folder, folderLength);
|
||||
continue;
|
||||
}
|
||||
@@ -391,8 +402,9 @@ void listFilesRecursively(uint32_t baseLength, FFstrbuf* folder, uint8_t indenta
|
||||
fputs(folder->chars + baseLength, stdout);
|
||||
}
|
||||
|
||||
puts(entry.cFileName);
|
||||
} while (FindNextFileA(hFind, &entry));
|
||||
FF_STRBUF_AUTO_DESTROY name = ffStrbufCreateWS(entry.cFileName);
|
||||
ffStrbufPutTo(&name, stdout);
|
||||
} while (FindNextFileW(hFind, &entry));
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user