Skip to content

Commit 6a79995

Browse files
committed
fix: avoid out of memory when verifying signatures
`Files.readAllBytes()` uses direct buffers internally which can be filled up quickly when called repeatedly in coroutines, as these buffers are not released quickly by the GC. The scenario can be reproduced by trying to login a couple of times one after the other with signature verification failing each time. Instead, we can avoid memory issues by streaming the cli and feed only blocks of bytes into the signature calculation.
1 parent 96663e6 commit 6a79995

File tree

1 file changed

+12
-6
lines changed

1 file changed

+12
-6
lines changed

src/main/kotlin/com/coder/toolbox/cli/gpg/GPGVerifier.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProv
2020
import java.io.ByteArrayInputStream
2121
import java.nio.file.Files
2222
import java.nio.file.Path
23+
import kotlin.io.path.inputStream
2324

2425
class GPGVerifier(
2526
private val context: CoderToolboxContext,
@@ -35,15 +36,14 @@ class GPGVerifier(
3536
return SignatureNotFound
3637
}
3738

38-
val (cliBytes, signatureBytes, publicKeyRing) = withContext(Dispatchers.IO) {
39-
val cliBytes = Files.readAllBytes(cli)
39+
val (signatureBytes, publicKeyRing) = withContext(Dispatchers.IO) {
4040
val signatureBytes = Files.readAllBytes(signature)
4141
val publicKeyRing = getCoderPublicKeyRings()
4242

43-
Triple(cliBytes, signatureBytes, publicKeyRing)
43+
Pair(signatureBytes, publicKeyRing)
4444
}
4545
return verifyDetachedSignature(
46-
cliBytes = cliBytes,
46+
cliPath = cli,
4747
signatureBytes = signatureBytes,
4848
publicKeyRings = publicKeyRing
4949
)
@@ -83,7 +83,7 @@ class GPGVerifier(
8383
* Verify a detached GPG signature
8484
*/
8585
fun verifyDetachedSignature(
86-
cliBytes: ByteArray,
86+
cliPath: Path,
8787
signatureBytes: ByteArray,
8888
publicKeyRings: List<PGPPublicKeyRing>
8989
): VerificationResult {
@@ -102,7 +102,13 @@ class GPGVerifier(
102102
?: throw PGPException("Public key not found for signature")
103103

104104
signature.init(JcaPGPContentVerifierBuilderProvider(), publicKey)
105-
signature.update(cliBytes)
105+
cliPath.inputStream().use { fileStream ->
106+
val buffer = ByteArray(8192)
107+
var bytesRead: Int
108+
while (fileStream.read(buffer).also { bytesRead = it } != -1) {
109+
signature.update(buffer, 0, bytesRead)
110+
}
111+
}
106112

107113
val isValid = signature.verify()
108114
context.logger.info("GPG signature verification result: $isValid")

0 commit comments

Comments
 (0)