Skip to content

Commit 39f0285

Browse files
committed
feat: add hooks for temporal parse and string compare in equals methods
1 parent 50a0e8f commit 39f0285

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed

src/main/java/com/code_intelligence/jazzer/runtime/TraceCmpHooks.java

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@
1919
import com.code_intelligence.jazzer.api.HookType;
2020
import com.code_intelligence.jazzer.api.MethodHook;
2121
import java.lang.invoke.MethodHandle;
22+
import java.time.Duration;
23+
import java.time.Instant;
24+
import java.time.LocalDate;
25+
import java.time.LocalDateTime;
26+
import java.time.LocalTime;
27+
import java.time.MonthDay;
28+
import java.time.OffsetDateTime;
29+
import java.time.OffsetTime;
30+
import java.time.Period;
31+
import java.time.ZoneId;
32+
import java.time.ZoneOffset;
33+
import java.time.ZonedDateTime;
34+
import java.time.Year;
35+
import java.time.YearMonth;
2236
import java.util.*;
2337

2438
@SuppressWarnings("unused")
@@ -234,6 +248,172 @@ public static void equals(
234248
}
235249
}
236250

251+
private static final LocalDate FALLBACK_LOCAL_DATE = LocalDate.of(2000, 1, 1);
252+
private static final ZonedDateTime FALLBACK_ZONED_DATE_TIME =
253+
ZonedDateTime.of(FALLBACK_LOCAL_DATE, LocalTime.MIDNIGHT, ZoneId.of("Europe/Paris"));
254+
private static final Map<Class<?>, String> TEMPORAL_PARSE_FALLBACKS =
255+
createTemporalParseFallbacks();
256+
257+
@MethodHook(
258+
type = HookType.AFTER,
259+
targetClassName = "java.time.Duration",
260+
targetMethod = "equals",
261+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
262+
@MethodHook(
263+
type = HookType.AFTER,
264+
targetClassName = "java.time.Instant",
265+
targetMethod = "equals",
266+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
267+
@MethodHook(
268+
type = HookType.AFTER,
269+
targetClassName = "java.time.LocalDate",
270+
targetMethod = "equals",
271+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
272+
@MethodHook(
273+
type = HookType.AFTER,
274+
targetClassName = "java.time.LocalDateTime",
275+
targetMethod = "equals",
276+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
277+
@MethodHook(
278+
type = HookType.AFTER,
279+
targetClassName = "java.time.LocalTime",
280+
targetMethod = "equals",
281+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
282+
@MethodHook(
283+
type = HookType.AFTER,
284+
targetClassName = "java.time.MonthDay",
285+
targetMethod = "equals",
286+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
287+
@MethodHook(
288+
type = HookType.AFTER,
289+
targetClassName = "java.time.OffsetDateTime",
290+
targetMethod = "equals",
291+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
292+
@MethodHook(
293+
type = HookType.AFTER,
294+
targetClassName = "java.time.OffsetTime",
295+
targetMethod = "equals",
296+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
297+
@MethodHook(
298+
type = HookType.AFTER,
299+
targetClassName = "java.time.Period",
300+
targetMethod = "equals",
301+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
302+
@MethodHook(
303+
type = HookType.AFTER,
304+
targetClassName = "java.time.Year",
305+
targetMethod = "equals",
306+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
307+
@MethodHook(
308+
type = HookType.AFTER,
309+
targetClassName = "java.time.YearMonth",
310+
targetMethod = "equals",
311+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
312+
@MethodHook(
313+
type = HookType.AFTER,
314+
targetClassName = "java.time.ZonedDateTime",
315+
targetMethod = "equals",
316+
targetMethodDescriptor = "(Ljava/lang/Object;)Z")
317+
public static void temporalEquals(
318+
MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean areEqual) {
319+
if (!areEqual
320+
&& arguments.length == 1
321+
&& arguments[0] != null
322+
&& thisObject.getClass() == arguments[0].getClass()) {
323+
TraceDataFlowNativeCallbacks.traceStrcmp(
324+
thisObject.toString(), arguments[0].toString(), 1, hookId);
325+
}
326+
}
327+
328+
@MethodHook(
329+
type = HookType.REPLACE,
330+
targetClassName = "java.time.Instant",
331+
targetMethod = "parse")
332+
@MethodHook(
333+
type = HookType.REPLACE,
334+
targetClassName = "java.time.LocalDate",
335+
targetMethod = "parse")
336+
@MethodHook(
337+
type = HookType.REPLACE,
338+
targetClassName = "java.time.LocalDateTime",
339+
targetMethod = "parse")
340+
@MethodHook(
341+
type = HookType.REPLACE,
342+
targetClassName = "java.time.LocalTime",
343+
targetMethod = "parse")
344+
@MethodHook(
345+
type = HookType.REPLACE,
346+
targetClassName = "java.time.OffsetDateTime",
347+
targetMethod = "parse")
348+
@MethodHook(
349+
type = HookType.REPLACE,
350+
targetClassName = "java.time.OffsetTime",
351+
targetMethod = "parse")
352+
@MethodHook(
353+
type = HookType.REPLACE,
354+
targetClassName = "java.time.ZonedDateTime",
355+
targetMethod = "parse")
356+
@MethodHook(
357+
type = HookType.REPLACE,
358+
targetClassName = "java.time.Year",
359+
targetMethod = "parse")
360+
@MethodHook(
361+
type = HookType.REPLACE,
362+
targetClassName = "java.time.YearMonth",
363+
targetMethod = "parse")
364+
@MethodHook(
365+
type = HookType.REPLACE,
366+
targetClassName = "java.time.MonthDay",
367+
targetMethod = "parse")
368+
@MethodHook(
369+
type = HookType.REPLACE,
370+
targetClassName = "java.time.Duration",
371+
targetMethod = "parse")
372+
@MethodHook(
373+
type = HookType.REPLACE,
374+
targetClassName = "java.time.Period",
375+
targetMethod = "parse")
376+
public static Object temporalParse(
377+
MethodHandle method, Object alwaysNull, Object[] arguments, int hookId) throws Throwable {
378+
try {
379+
return method.invokeWithArguments(arguments);
380+
} catch (Throwable throwable) {
381+
if (throwable instanceof Error) {
382+
throw throwable;
383+
}
384+
385+
386+
if (arguments[0] != null) {
387+
String fallback = TEMPORAL_PARSE_FALLBACKS.get(method.type().returnType());
388+
if (fallback != null) {
389+
TraceDataFlowNativeCallbacks.traceStrcmp(arguments[0].toString(), fallback, 1, hookId);
390+
}
391+
}
392+
throw throwable;
393+
}
394+
}
395+
396+
private static Map<Class<?>, String> createTemporalParseFallbacks() {
397+
Map<Class<?>, String> fallbacks = new HashMap<>();
398+
fallbacks.put(Duration.class, Duration.ZERO.toString());
399+
fallbacks.put(Instant.class, Instant.EPOCH.toString());
400+
fallbacks.put(LocalDate.class, FALLBACK_LOCAL_DATE.toString());
401+
fallbacks.put(LocalDateTime.class, FALLBACK_LOCAL_DATE.atStartOfDay().toString());
402+
fallbacks.put(LocalTime.class, LocalTime.MIDNIGHT.toString());
403+
fallbacks.put(
404+
OffsetDateTime.class,
405+
OffsetDateTime.of(FALLBACK_LOCAL_DATE, LocalTime.MIDNIGHT, ZoneOffset.UTC).toString());
406+
fallbacks.put(OffsetTime.class, OffsetTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC).toString());
407+
fallbacks.put(Period.class, Period.ZERO.toString());
408+
fallbacks.put(Year.class, Year.of(FALLBACK_LOCAL_DATE.getYear()).toString());
409+
fallbacks.put(
410+
YearMonth.class,
411+
YearMonth.of(FALLBACK_LOCAL_DATE.getYear(), FALLBACK_LOCAL_DATE.getMonth()).toString());
412+
fallbacks.put(MonthDay.class, MonthDay.from(FALLBACK_LOCAL_DATE).toString());
413+
fallbacks.put(ZonedDateTime.class, FALLBACK_ZONED_DATE_TIME.toString());
414+
return Collections.unmodifiableMap(fallbacks);
415+
}
416+
237417
@MethodHook(type = HookType.AFTER, targetClassName = "java.util.Objects", targetMethod = "equals")
238418
public static void genericObjectsEquals(
239419
MethodHandle method, Object thisObject, Object[] arguments, int hookId, Boolean areEqual) {

tests/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,20 @@ java_fuzz_target_test(
774774
],
775775
)
776776

777+
java_fuzz_target_test(
778+
name = "TimeParseFuzzer",
779+
srcs = ["src/test/java/com/example/TimeParseFuzzer.java"],
780+
allowed_findings = ["com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium"],
781+
fuzzer_args = [
782+
"-runs=1000000",
783+
],
784+
target_class = "com.example.TimeParseFuzzer",
785+
verify_crash_reproducer = False,
786+
deps = [
787+
"//src/main/java/com/code_intelligence/jazzer/mutation/annotation",
788+
],
789+
)
790+
777791
sh_test(
778792
name = "jazzer_from_path_test",
779793
srcs = ["src/test/shell/jazzer_from_path_test.sh"],
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2024 Code Intelligence GmbH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example;
18+
19+
import com.code_intelligence.jazzer.api.FuzzerSecurityIssueMedium;
20+
import com.code_intelligence.jazzer.mutation.annotation.Ascii;
21+
import com.code_intelligence.jazzer.mutation.annotation.NotNull;
22+
import java.time.OffsetDateTime;
23+
import java.time.format.DateTimeParseException;
24+
25+
public final class TimeParseFuzzer {
26+
private static final OffsetDateTime TARGET_DATE_TIME =
27+
OffsetDateTime.parse("2001-12-04T00:00:00-05:00");
28+
29+
private TimeParseFuzzer() {}
30+
31+
public static void fuzzerTestOneInput(@NotNull @Ascii String offsetDateInput) {
32+
try {
33+
OffsetDateTime offsetDateTime = OffsetDateTime.parse(offsetDateInput);
34+
if (TARGET_DATE_TIME.equals(offsetDateTime)) {
35+
throw new FuzzerSecurityIssueMedium();
36+
}
37+
} catch (DateTimeParseException ignored) {}
38+
}
39+
}

0 commit comments

Comments
 (0)