1 // © 2023 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3
4 // Fuzzer for ICU Calendar.
5
6 #include <cstring>
7
8 #include "fuzzer_utils.h"
9
10 #include "unicode/calendar.h"
11 #include "unicode/localebuilder.h"
12 #include "unicode/locid.h"
13
CreateRandomTimeZone(uint16_t rnd)14 icu::TimeZone* CreateRandomTimeZone(uint16_t rnd) {
15 icu::Locale und("und");
16 UErrorCode status = U_ZERO_ERROR;
17 std::unique_ptr<icu::StringEnumeration> enumeration(
18 icu::TimeZone::createEnumeration(status));
19 if (U_SUCCESS(status)) {
20 int32_t count = enumeration->count(status);
21 if (U_SUCCESS(status)) {
22 int32_t i = rnd % count;
23 const icu::UnicodeString* id = nullptr;
24 do {
25 id = enumeration->snext(status);
26 } while (U_SUCCESS(status) && --i > 0);
27 if (U_SUCCESS(status)) {
28 return icu::TimeZone::createTimeZone(*id);
29 }
30 }
31 }
32 return icu::TimeZone::getGMT()->clone();
33 }
GetRandomCalendarType(uint8_t rnd)34 const char* GetRandomCalendarType(uint8_t rnd) {
35 icu::Locale und("und");
36 UErrorCode status = U_ZERO_ERROR;
37 std::unique_ptr<icu::StringEnumeration> enumeration(
38 icu::Calendar::getKeywordValuesForLocale("calendar", und, false, status));
39 const char* type = "";
40 if (U_SUCCESS(status)) {
41 int32_t count = enumeration->count(status);
42 if (U_SUCCESS(status)) {
43 int32_t i = rnd % count;
44 do {
45 type = enumeration->next(nullptr, status);
46 } while (U_SUCCESS(status) && --i > 0);
47 }
48 }
49 type = uloc_toUnicodeLocaleType("ca", type);
50 return type;
51 }
52
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)53 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
54 uint16_t rnd;
55 // Set the limit for the test data to 1000 bytes to avoid timeout for a
56 // very long list of operations.
57 if (size > 1000) { size = 1000; }
58 if (size < 2*sizeof(rnd) + 1) return 0;
59 icu::StringPiece fuzzData(reinterpret_cast<const char *>(data), size);
60 // Byte 0 and 1 randomly select a TimeZone
61 std::memcpy(&rnd, fuzzData.data(), sizeof(rnd));
62 fuzzData.remove_prefix(sizeof(rnd));
63 std::unique_ptr<icu::TimeZone> timeZone(CreateRandomTimeZone(rnd));
64
65 // Byte 1 and 2 randomly select a Locale
66 std::memcpy(&rnd, fuzzData.data(), sizeof(rnd));
67 fuzzData.remove_prefix(sizeof(rnd));
68 icu::Locale locale = GetRandomLocale(rnd);
69
70 // Byte 4 randomly select a Calendar type
71 const char* type = GetRandomCalendarType(*fuzzData.data());
72 fuzzData.remove_prefix(1);
73
74 UErrorCode status = U_ZERO_ERROR;
75 icu::LocaleBuilder bld;
76 bld.setLocale(locale);
77 bld.setUnicodeLocaleKeyword("ca", type);
78 locale = bld.build(status);
79 if (U_FAILURE(status)) return 0;
80 std::unique_ptr<icu::Calendar> cal(
81 icu::Calendar::createInstance(*timeZone, locale, status));
82 printf("locale = %s\n", locale.getName());
83 if (U_FAILURE(status)) return 0;
84 cal->clear();
85
86 int32_t amount;
87 double time;
88 while (fuzzData.length() > 2 + static_cast<int32_t>(sizeof(time))) {
89 UCalendarDateFields field = static_cast<UCalendarDateFields>(
90 (*fuzzData.data()) % UCAL_FIELD_COUNT);
91 fuzzData.remove_prefix(1);
92
93 uint8_t command = *fuzzData.data();
94 fuzzData.remove_prefix(1);
95
96 std::memcpy(&time, fuzzData.data(), sizeof(time));
97 std::memcpy(&amount, fuzzData.data(), sizeof(amount));
98 fuzzData.remove_prefix(sizeof(time));
99
100 status = U_ZERO_ERROR;
101 switch (command % 7) {
102 case 0:
103 printf("setTime(%f)\n", time);
104 cal->setTime(time, status);
105 break;
106 case 1:
107 printf("getTime()\n");
108 cal->getTime(status);
109 break;
110 case 2:
111 printf("set(%d, %d)\n", field, amount);
112 cal->set(field, amount);
113 break;
114 case 3:
115 printf("add(%d, %d)\n", field, amount);
116 cal->add(field, amount, status);
117 break;
118 case 4:
119 printf("roll(%d, %d)\n", field, amount);
120 cal->roll(field, amount, status);
121 break;
122 case 5:
123 printf("fieldDifference(%f, %d)\n", time, field);
124 cal->fieldDifference(time, field, status);
125 break;
126 case 6:
127 printf("get(%d)\n", field);
128 cal->get(field, status);
129 break;
130 default:
131 break;
132 }
133 }
134
135 return EXIT_SUCCESS;
136 }
137