fix: avoid StackOverflowError when parsing upload payload

This commit is contained in:
liumangmang
2026-03-10 18:07:07 +08:00
parent db806aef09
commit 4e0fb60eef
2 changed files with 103 additions and 12 deletions

View File

@@ -1,6 +0,0 @@
---
alwaysApply: true
---
Please execute the generate commit message command in Chinese.
生成 Git 提交信息Commit Message必须强制使用中文。

View File

@@ -51,7 +51,6 @@ public class RedisClipSync {
// 公共上传通道
private static final String UPLOAD_CHANNEL = "global_clip_upload";
private static final String UNKNOWN_SLAVE_ID = "unknown";
private static final Pattern UPLOAD_PAYLOAD_PATTERN = Pattern.compile("\\{\"slaveId\":\"((?:\\\\.|[^\"])*)\",\"content\":\"((?:\\\\.|[^\"])*)\",\"timestamp\":(\\d+)\\}");
private static final String MASTER_TO_SLAVE_LOG_FILE = "master_to_slave.log";
private static final String SLAVE_TO_MASTER_LOG_FILE = "slave_to_master.log";
private static final String LOG_ENTRY_SEPARATOR = "\n\n------------------\n\n";
@@ -551,14 +550,112 @@ public class RedisClipSync {
return null;
}
Matcher matcher = UPLOAD_PAYLOAD_PATTERN.matcher(message);
if (!matcher.matches()) {
// Avoid regex here: large clipboard payloads can trigger deep backtracking and StackOverflowError.
// We only need to parse a fixed JSON shape produced by buildUploadPayload().
try {
String trimmed = message.trim();
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
return null;
}
String slaveIdRaw = extractJsonStringField(trimmed, "slaveId");
String contentRaw = extractJsonStringField(trimmed, "content");
// timestamp is optional for parsing; we just validate it exists and is numeric when present
if (!hasJsonNumberField(trimmed, "timestamp")) {
return null;
}
String slaveId = unescapeJson(slaveIdRaw);
String content = unescapeJson(contentRaw);
return new UploadMessage(slaveId, content);
} catch (Exception ignored) {
return null;
}
}
String slaveId = unescapeJson(matcher.group(1));
String content = unescapeJson(matcher.group(2));
return new UploadMessage(slaveId, content);
static String extractJsonStringField(String json, String fieldName) {
int namePos = indexOfJsonFieldName(json, fieldName);
if (namePos < 0) {
throw new IllegalArgumentException("missing field: " + fieldName);
}
int colon = json.indexOf(':', namePos);
if (colon < 0) {
throw new IllegalArgumentException("bad json: missing colon for " + fieldName);
}
int i = colon + 1;
while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
i++;
}
if (i >= json.length() || json.charAt(i) != '"') {
throw new IllegalArgumentException("bad json: expected string for " + fieldName);
}
return parseJsonStringBody(json, i);
}
static boolean hasJsonNumberField(String json, String fieldName) {
int namePos = indexOfJsonFieldName(json, fieldName);
if (namePos < 0) {
return false;
}
int colon = json.indexOf(':', namePos);
if (colon < 0) {
return false;
}
int i = colon + 1;
while (i < json.length() && Character.isWhitespace(json.charAt(i))) {
i++;
}
int start = i;
while (i < json.length()) {
char c = json.charAt(i);
if (c < '0' || c > '9') {
break;
}
i++;
}
return i > start;
}
static int indexOfJsonFieldName(String json, String fieldName) {
// Find: "fieldName" (as a JSON string token)
String needle = "\"" + fieldName + "\"";
return json.indexOf(needle);
}
/**
* Parse JSON string starting at the opening quote and return the raw (escaped) body.
* Example input: "a\\n\"b" -> returns: a\\n\"b (without surrounding quotes)
*/
static String parseJsonStringBody(String json, int openingQuoteIndex) {
if (openingQuoteIndex < 0 || openingQuoteIndex >= json.length() || json.charAt(openingQuoteIndex) != '"') {
throw new IllegalArgumentException("bad json: expected opening quote");
}
StringBuilder rawEscaped = new StringBuilder();
int i = openingQuoteIndex + 1;
while (i < json.length()) {
char c = json.charAt(i);
if (c == '"') {
return rawEscaped.toString();
}
if (c == '\\') {
if (i + 1 >= json.length()) {
throw new IllegalArgumentException("bad json: trailing backslash");
}
rawEscaped.append('\\').append(json.charAt(i + 1));
i += 2;
continue;
}
rawEscaped.append(c);
i++;
}
throw new IllegalArgumentException("bad json: unterminated string");
}
static String escapeJson(String value) {