; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=instcombine -S | FileCheck %s ; ; Verify that calls with arguments with pointers just past the end of ; a string to [a subset of] library functions that expect nul-terminated ; strings as arguments are folded to safe values. The rationale is that ; since they are undefined and even though folding them isn't important ; for efficiency and prevents sanitizers from detecting and reporting ; them, sanitizers usually don't run, and transforming such invalid ; calls to something valid is safer than letting the program run off ; the rails. See the Safe Optimizations for Sanitizers RFC for ; an in-depth discussion of the trade-offs: ; https://discourse.llvm.org/t/rfc-safe-optimizations-for-sanitizers declare i8* @strchr(i8*, i32) declare i8* @strrchr(i8*, i32) declare i32 @strcmp(i8*, i8*) declare i32 @strncmp(i8*, i8*, i64) declare i8* @strstr(i8*, i8*) declare i8* @stpcpy(i8*, i8*) declare i8* @strcpy(i8*, i8*) declare i8* @stpncpy(i8*, i8*, i64) declare i8* @strncpy(i8*, i8*, i64) declare i64 @strlen(i8*) declare i64 @strnlen(i8*, i64) declare i8* @strpbrk(i8*, i8*) declare i64 @strspn(i8*, i8*) declare i64 @strcspn(i8*, i8*) declare i32 @atoi(i8*) declare i64 @atol(i8*) declare i64 @atoll(i8*) declare i64 @strtol(i8*, i8**, i32) declare i64 @strtoll(i8*, i8**, i32) declare i64 @strtoul(i8*, i8**, i32) declare i64 @strtoull(i8*, i8**, i32) declare i32 @sprintf(i8*, i8*, ...) declare i32 @snprintf(i8*, i64, i8*, ...) @a5 = constant [5 x i8] c"%s\0045"; ; Fold strchr(a5 + 5, '\0') to null. define i8* @fold_strchr_past_end() { ; CHECK-LABEL: @fold_strchr_past_end( ; CHECK-NEXT: ret i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0) ; %p = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %q = call i8* @strchr(i8* %p, i32 0) ret i8* %q } ; Fold strcmp(a5, a5 + 5) (and vice versa) to null. define void @fold_strcmp_past_end(i32* %pcmp) { ; CHECK-LABEL: @fold_strcmp_past_end( ; CHECK-NEXT: store i32 1, i32* [[PCMP:%.*]], align 4 ; CHECK-NEXT: [[PC50:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1 ; CHECK-NEXT: store i32 -1, i32* [[PC50]], align 4 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %c05 = call i32 @strcmp(i8* %p0, i8* %p5) %pc05 = getelementptr i32, i32* %pcmp, i32 0 store i32 %c05, i32* %pc05 %c50 = call i32 @strcmp(i8* %p5, i8* %p0) %pc50 = getelementptr i32, i32* %pcmp, i32 1 store i32 %c50, i32* %pc50 ret void } ; Likewise, fold strncmp(a5, a5 + 5, 5) (and vice versa) to null. define void @fold_strncmp_past_end(i32* %pcmp) { ; CHECK-LABEL: @fold_strncmp_past_end( ; CHECK-NEXT: store i32 1, i32* [[PCMP:%.*]], align 4 ; CHECK-NEXT: [[PC50:%.*]] = getelementptr i32, i32* [[PCMP]], i64 1 ; CHECK-NEXT: store i32 -1, i32* [[PC50]], align 4 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %c05 = call i32 @strncmp(i8* %p0, i8* %p5, i64 5) %pc05 = getelementptr i32, i32* %pcmp, i32 0 store i32 %c05, i32* %pc05 %c50 = call i32 @strncmp(i8* %p5, i8* %p0, i64 5) %pc50 = getelementptr i32, i32* %pcmp, i32 1 store i32 %c50, i32* %pc50 ret void } ; Fold strrchr(a5 + 5, '\0') to null. define i8* @fold_strrchr_past_end(i32 %c) { ; CHECK-LABEL: @fold_strrchr_past_end( ; CHECK-NEXT: ret i8* null ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %r = call i8* @strrchr(i8* %p5, i32 0) ret i8* %r } ; Fold strstr(a5 + 5, a5) (and vice versa) to null. define void @fold_strstr_past_end(i8** %psub) { ; CHECK-LABEL: @fold_strstr_past_end( ; CHECK-NEXT: store i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8** [[PSUB:%.*]], align 8 ; CHECK-NEXT: [[PS50:%.*]] = getelementptr i8*, i8** [[PSUB]], i64 1 ; CHECK-NEXT: store i8* null, i8** [[PS50]], align 8 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %s05 = call i8* @strstr(i8* %p0, i8* %p5) %ps05 = getelementptr i8*, i8** %psub, i32 0 store i8* %s05, i8** %ps05 %s50 = call i8* @strstr(i8* %p5, i8* %p0) %ps50 = getelementptr i8*, i8** %psub, i32 1 store i8* %s50, i8** %ps50 ret void } ; Fold strlen(a5 + 5) to 0. define i64 @fold_strlen_past_end() { ; CHECK-LABEL: @fold_strlen_past_end( ; CHECK-NEXT: ret i64 0 ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %r = call i64 @strlen(i8* %p5) ret i64 %r } ; TODO: Fold stpcpy(dst, a5 + 5) to (*dst = '\0', dst). define i8* @fold_stpcpy_past_end(i8* %dst) { ; CHECK-LABEL: @fold_stpcpy_past_end( ; CHECK-NEXT: ret i8* [[DST:%.*]] ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %r = call i8* @strcpy(i8* %dst, i8* %p5) ret i8* %r } ; TODO: Fold strcpy(dst, a5 + 5) to (*dst = '\0', dst). define i8* @fold_strcpy_past_end(i8* %dst) { ; CHECK-LABEL: @fold_strcpy_past_end( ; CHECK-NEXT: ret i8* [[DST:%.*]] ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %r = call i8* @strcpy(i8* %dst, i8* %p5) ret i8* %r } ; TODO: Fold stpncpy(dst, a5 + 5, 5) to (memset(dst, 0, 5), dst + 5). define i8* @fold_stpncpy_past_end(i8* %dst) { ; CHECK-LABEL: @fold_stpncpy_past_end( ; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) [[DST:%.*]], i8 0, i64 5, i1 false) ; CHECK-NEXT: ret i8* [[DST]] ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %r = call i8* @strncpy(i8* %dst, i8* %p5, i64 5) ret i8* %r } ; TODO: Fold strncpy(dst, a5 + 5, 5) to memset(dst, 0, 5). define i8* @fold_strncpy_past_end(i8* %dst) { ; CHECK-LABEL: @fold_strncpy_past_end( ; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 1 dereferenceable(5) [[DST:%.*]], i8 0, i64 5, i1 false) ; CHECK-NEXT: ret i8* [[DST]] ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %r = call i8* @strncpy(i8* %dst, i8* %p5, i64 5) ret i8* %r } ; Fold strpbrk(a5, a5 + 5) (and vice versa) to null. define void @fold_strpbrk_past_end(i8** %psub) { ; CHECK-LABEL: @fold_strpbrk_past_end( ; CHECK-NEXT: store i8* null, i8** [[PSUB:%.*]], align 8 ; CHECK-NEXT: [[PS50:%.*]] = getelementptr i8*, i8** [[PSUB]], i64 1 ; CHECK-NEXT: store i8* null, i8** [[PS50]], align 8 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %s05 = call i8* @strpbrk(i8* %p0, i8* %p5) %ps05 = getelementptr i8*, i8** %psub, i32 0 store i8* %s05, i8** %ps05 %s50 = call i8* @strpbrk(i8* %p5, i8* %p0) %ps50 = getelementptr i8*, i8** %psub, i32 1 store i8* %s50, i8** %ps50 ret void } ; Fold strspn(a5, a5 + 5) (and vice versa) to null. define void @fold_strspn_past_end(i64* %poff) { ; CHECK-LABEL: @fold_strspn_past_end( ; CHECK-NEXT: store i64 0, i64* [[POFF:%.*]], align 4 ; CHECK-NEXT: [[PO50:%.*]] = getelementptr i64, i64* [[POFF]], i64 1 ; CHECK-NEXT: store i64 0, i64* [[PO50]], align 4 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %o05 = call i64 @strspn(i8* %p0, i8* %p5) %po05 = getelementptr i64, i64* %poff, i32 0 store i64 %o05, i64* %po05 %o50 = call i64 @strspn(i8* %p5, i8* %p0) %po50 = getelementptr i64, i64* %poff, i32 1 store i64 %o50, i64* %po50 ret void } ; Fold strcspn(a5, a5 + 5) (and vice versa) to null. define void @fold_strcspn_past_end(i64* %poff) { ; CHECK-LABEL: @fold_strcspn_past_end( ; CHECK-NEXT: store i64 2, i64* [[POFF:%.*]], align 4 ; CHECK-NEXT: [[PO50:%.*]] = getelementptr i64, i64* [[POFF]], i64 1 ; CHECK-NEXT: store i64 0, i64* [[PO50]], align 4 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %o05 = call i64 @strcspn(i8* %p0, i8* %p5) %po05 = getelementptr i64, i64* %poff, i32 0 store i64 %o05, i64* %po05 %o50 = call i64 @strcspn(i8* %p5, i8* %p0) %po50 = getelementptr i64, i64* %poff, i32 1 store i64 %o50, i64* %po50 ret void } ; TODO: Fold the 32-bit atoi(a5 + 5) to zero. ; Verify that processing the invalid call doesn't run into trouble. define i32 @fold_atoi_past_end() { ; CHECK-LABEL: @fold_atoi_past_end( ; CHECK-NEXT: [[I:%.*]] = call i32 @atoi(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) ; CHECK-NEXT: ret i32 [[I]] ; %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %i = call i32 @atoi(i8* %p5) ret i32 %i } ; TODO: Likewise, fold the 64-bit atol(a5 + 5) to zero, and similarly ; for atoll and strtrol and similar. ; Verify that processing the invalid call doesn't run into trouble. define void @fold_atol_strtol_past_end(i64* %ps) { ; CHECK-LABEL: @fold_atol_strtol_past_end( ; CHECK-NEXT: [[I0:%.*]] = call i64 @atol(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) ; CHECK-NEXT: store i64 [[I0]], i64* [[PS:%.*]], align 4 ; CHECK-NEXT: [[I1:%.*]] = call i64 @atoll(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) ; CHECK-NEXT: [[P1:%.*]] = getelementptr i64, i64* [[PS]], i64 1 ; CHECK-NEXT: store i64 [[I1]], i64* [[P1]], align 4 ; CHECK-NEXT: [[I2:%.*]] = call i64 @strtol(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8** null, i32 0) ; CHECK-NEXT: [[P2:%.*]] = getelementptr i64, i64* [[PS]], i64 2 ; CHECK-NEXT: store i64 [[I2]], i64* [[P2]], align 4 ; CHECK-NEXT: [[I3:%.*]] = call i64 @strtoul(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8** null, i32 8) ; CHECK-NEXT: [[P3:%.*]] = getelementptr i64, i64* [[PS]], i64 3 ; CHECK-NEXT: store i64 [[I3]], i64* [[P3]], align 4 ; CHECK-NEXT: [[I4:%.*]] = call i64 @strtoll(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8** null, i32 10) ; CHECK-NEXT: [[P4:%.*]] = getelementptr i64, i64* [[PS]], i64 4 ; CHECK-NEXT: store i64 [[I4]], i64* [[P4]], align 4 ; CHECK-NEXT: [[I5:%.*]] = call i64 @strtoul(i8* nocapture getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0), i8** null, i32 16) ; CHECK-NEXT: [[P5:%.*]] = getelementptr i64, i64* [[PS]], i64 5 ; CHECK-NEXT: store i64 [[I5]], i64* [[P5]], align 4 ; CHECK-NEXT: ret void ; %pa5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %i0 = call i64 @atol(i8* %pa5) %p0 = getelementptr i64, i64* %ps, i32 0 store i64 %i0, i64* %p0 %i1 = call i64 @atoll(i8* %pa5) %p1 = getelementptr i64, i64* %ps, i32 1 store i64 %i1, i64* %p1 %i2 = call i64 @strtol(i8* %pa5, i8** null, i32 0) %p2 = getelementptr i64, i64* %ps, i32 2 store i64 %i2, i64* %p2 %i3 = call i64 @strtoul(i8* %pa5, i8** null, i32 8) %p3 = getelementptr i64, i64* %ps, i32 3 store i64 %i3, i64* %p3 %i4 = call i64 @strtoll(i8* %pa5, i8** null, i32 10) %p4 = getelementptr i64, i64* %ps, i32 4 store i64 %i4, i64* %p4 %i5 = call i64 @strtoul(i8* %pa5, i8** null, i32 16) %p5 = getelementptr i64, i64* %ps, i32 5 store i64 %i5, i64* %p5 ret void } ; Fold sprintf(dst, a5 + 5) to zero, and also ; TODO: fold sprintf(dst, "%s", a5 + 5) to zero. define void @fold_sprintf_past_end(i32* %pcnt, i8* %dst) { ; CHECK-LABEL: @fold_sprintf_past_end( ; CHECK-NEXT: store i32 0, i32* [[PCNT:%.*]], align 4 ; CHECK-NEXT: [[PN05:%.*]] = getelementptr i32, i32* [[PCNT]], i64 1 ; CHECK-NEXT: store i32 0, i32* [[PN05]], align 4 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %n5_ = call i32 (i8*, i8*, ...) @sprintf(i8* %dst, i8* %p5) %pn5_ = getelementptr i32, i32* %pcnt, i32 0 store i32 %n5_, i32* %pn5_ %n05 = call i32 (i8*, i8*, ...) @sprintf(i8* %dst, i8* %p0, i8* %p5) %pn05 = getelementptr i32, i32* %pcnt, i32 1 store i32 %n05, i32* %pn05 ret void } ; Fold snprintf(dst, n, a5 + 5) to zero, and also ; TODO: fold snprintf(dst, n, "%s", a5 + 5) to zero. define void @fold_snprintf_past_end(i32* %pcnt, i8* %dst, i64 %n) { ; CHECK-LABEL: @fold_snprintf_past_end( ; CHECK-NEXT: [[N5_:%.*]] = call i32 (i8*, i64, i8*, ...) @snprintf(i8* [[DST:%.*]], i64 [[N:%.*]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) ; CHECK-NEXT: store i32 [[N5_]], i32* [[PCNT:%.*]], align 4 ; CHECK-NEXT: [[N05:%.*]] = call i32 (i8*, i64, i8*, ...) @snprintf(i8* [[DST]], i64 [[N]], i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 0, i64 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @a5, i64 1, i64 0)) ; CHECK-NEXT: [[PN05:%.*]] = getelementptr i32, i32* [[PCNT]], i64 1 ; CHECK-NEXT: store i32 [[N05]], i32* [[PN05]], align 4 ; CHECK-NEXT: ret void ; %p0 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 0 %p5 = getelementptr [5 x i8], [5 x i8]* @a5, i32 0, i32 5 %n5_ = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %dst, i64 %n, i8* %p5) %pn5_ = getelementptr i32, i32* %pcnt, i32 0 store i32 %n5_, i32* %pn5_ %n05 = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %dst, i64 %n, i8* %p0, i8* %p5) %pn05 = getelementptr i32, i32* %pcnt, i32 1 store i32 %n05, i32* %pn05 ret void }