forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathPrintf.qll
More file actions
1448 lines (1321 loc) · 50.3 KB
/
Printf.qll
File metadata and controls
1448 lines (1321 loc) · 50.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* A library for dealing with `printf`-like formatting strings.
*/
import semmle.code.cpp.Type
import semmle.code.cpp.commons.CommonType
import semmle.code.cpp.commons.StringAnalysis
import semmle.code.cpp.models.interfaces.FormattingFunction
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
private newtype TBufferWriteEstimationReason =
TUnspecifiedEstimateReason() or
TTypeBoundsAnalysis() or
TWidenedValueFlowAnalysis() or
TValueFlowAnalysis()
private predicate gradeToReason(int grade, TBufferWriteEstimationReason reason) {
// when combining reasons, lower grade takes precedence
grade = 0 and reason = TUnspecifiedEstimateReason()
or
grade = 1 and reason = TTypeBoundsAnalysis()
or
grade = 2 and reason = TWidenedValueFlowAnalysis()
or
grade = 3 and reason = TValueFlowAnalysis()
}
/**
* A reason for a specific buffer write size estimate.
*/
abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
/**
* Returns the name of the concrete class.
*/
abstract string toString();
/**
* Returns a human readable representation of this reason.
*/
abstract string getDescription();
/**
* Combine estimate reasons. Used to give a reason for the size of a format string
* conversion given reasons coming from its individual specifiers.
*/
BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
exists(int grade, int otherGrade |
gradeToReason(grade, this) and gradeToReason(otherGrade, other)
|
if otherGrade < grade then result = other else result = this
)
}
}
/**
* No particular reason given. This is currently used for backward compatibility so that
* classes derived from BufferWrite and overriding `getMaxData/0` still work with the
* queries as intended.
*/
class UnspecifiedEstimateReason extends BufferWriteEstimationReason, TUnspecifiedEstimateReason {
override string toString() { result = "UnspecifiedEstimateReason" }
override string getDescription() { result = "no reason specified" }
}
/**
* The estimation comes from rough bounds just based on the type (e.g.
* `0 <= x < 2^32` for an unsigned 32 bit integer).
*/
class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysis {
override string toString() { result = "TypeBoundsAnalysis" }
override string getDescription() { result = "based on type bounds" }
}
/**
* The estimation comes from non trivial bounds found via actual flow analysis,
* but a widening approximation might have been used for variables in loops.
* For example
* ```
* for (int i = 0; i < 10; ++i) {
* int j = i + i;
* //... <- estimation done here based on j
* }
* ```
*/
class WidenedValueFlowAnalysis extends BufferWriteEstimationReason, TWidenedValueFlowAnalysis {
override string toString() { result = "WidenedValueFlowAnalysis" }
override string getDescription() {
result = "based on flow analysis of value bounds with a widening approximation"
}
}
/**
* The estimation comes from non trivial bounds found via actual flow analysis.
* For example
* ```
* unsigned u = x;
* if (u < 1000) {
* //... <- estimation done here based on u
* }
* ```
*/
class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis {
override string toString() { result = "ValueFlowAnalysis" }
override string getDescription() { result = "based on flow analysis of value bounds" }
}
class PrintfFormatAttribute extends FormatAttribute {
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
}
/**
* A function that can be identified as a `printf` style formatting
* function by its use of the GNU `format` attribute.
*/
class AttributeFormattingFunction extends FormattingFunction {
override string getAPrimaryQlClass() { result = "AttributeFormattingFunction" }
AttributeFormattingFunction() {
exists(PrintfFormatAttribute printf_attrib |
printf_attrib = this.getAnAttribute() and
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
)
}
override int getFormatParameterIndex() {
forex(PrintfFormatAttribute printf_attrib | printf_attrib = this.getAnAttribute() |
result = printf_attrib.getFormatIndex()
)
}
}
/**
* A standard function such as `vprintf` that has a format parameter
* and a variable argument list of type `va_arg`. `formatParamIndex` indicates
* the format parameter and `type` indicates the type of `vprintf`:
* - `""` is a `vprintf` variant, `outputParamIndex` is `-1`.
* - `"f"` is a `vfprintf` variant, `outputParamIndex` indicates the output stream parameter.
* - `"s"` is a `vsprintf` variant, `outputParamIndex` indicates the output buffer parameter.
* - `"?"` if the type cannot be determined. `outputParamIndex` is `-1`.
*/
predicate primitiveVariadicFormatter(
TopLevelFunction f, string type, int formatParamIndex, int outputParamIndex
) {
type = f.getName().regexpCapture("_?_?va?([fs]?)n?w?printf(_s)?(_p)?(_l)?", 1) and
(
if f.getName().matches("%\\_l")
then formatParamIndex = f.getNumberOfParameters() - 3
else formatParamIndex = f.getNumberOfParameters() - 2
) and
(
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
) and
not (
// exclude functions with an implementation in the snapshot source
// directory, as they may not be standard implementations.
exists(f.getBlock()) and
exists(f.getFile().getRelativePath())
)
}
/**
* Gets a function call whose target is a variadic formatter with the given
* `type`, `format` parameter index and `output` parameter index.
*
* Join-order helper for `callsVariadicFormatter`.
*/
pragma[nomagic]
private predicate callsVariadicFormatterCall(FunctionCall fc, string type, int format, int output) {
variadicFormatter(fc.getTarget(), type, format, output)
}
private predicate callsVariadicFormatter(
Function f, string type, int formatParamIndex, int outputParamIndex
) {
// calls a variadic formatter with `formatParamIndex`, `outputParamIndex` linked
exists(FunctionCall fc, int format, int output |
callsVariadicFormatterCall(fc, type, format, output) and
fc.getEnclosingFunction() = f and
fc.getArgument(format) = f.getParameter(formatParamIndex).getAnAccess() and
fc.getArgument(output) = f.getParameter(outputParamIndex).getAnAccess()
)
or
// calls a variadic formatter with only `formatParamIndex` linked
exists(FunctionCall fc, string calledType, int format, int output |
callsVariadicFormatterCall(fc, calledType, format, output) and
fc.getEnclosingFunction() = f and
fc.getArgument(format) = f.getParameter(formatParamIndex).getAnAccess() and
not fc.getArgument(output) = f.getParameter(_).getAnAccess() and
(
calledType = "" and
type = ""
or
calledType != "" and
type = "?" // we probably should have an `outputParamIndex` link but have lost it.
) and
outputParamIndex = -1
)
}
/**
* Holds if `f` is a function such as `vprintf` that has a format parameter
* and a variable argument list of type `va_arg`. `formatParamIndex` indicates
* the format parameter and `type` indicates the type of `vprintf`:
* - `""` is a `vprintf` variant, `outputParamIndex` is `-1`.
* - `"f"` is a `vfprintf` variant, `outputParamIndex` indicates the output stream parameter.
* - `"s"` is a `vsprintf` variant, `outputParamIndex` indicates the output buffer parameter.
* - `"?"` if the type cannot be determined. `outputParamIndex` is `-1`.
*/
predicate variadicFormatter(Function f, string type, int formatParamIndex, int outputParamIndex) {
primitiveVariadicFormatter(f, type, formatParamIndex, outputParamIndex)
or
not f.isVarargs() and
callsVariadicFormatter(f, type, formatParamIndex, outputParamIndex)
}
/**
* A function not in the standard library which takes a `printf`-like formatting
* string and a variable number of arguments.
*/
class UserDefinedFormattingFunction extends FormattingFunction {
override string getAPrimaryQlClass() { result = "UserDefinedFormattingFunction" }
UserDefinedFormattingFunction() { this.isVarargs() and callsVariadicFormatter(this, _, _, _) }
override int getFormatParameterIndex() { callsVariadicFormatter(this, _, result, _) }
override int getOutputParameterIndex(boolean isStream) {
callsVariadicFormatter(this, "f", _, result) and isStream = true
or
callsVariadicFormatter(this, "s", _, result) and isStream = false
}
override predicate isOutputGlobal() { callsVariadicFormatter(this, "", _, _) }
}
/**
* A call to one of the formatting functions.
*/
class FormattingFunctionCall extends Expr {
FormattingFunctionCall() { this.(Call).getTarget() instanceof FormattingFunction }
override string getAPrimaryQlClass() { result = "FormattingFunctionCall" }
/**
* Gets the formatting function being called.
*/
FormattingFunction getTarget() { result = this.(Call).getTarget() }
/**
* Gets the `i`th argument for this call.
*
* The range of `i` is from `0` to `getNumberOfArguments() - 1`.
*/
Expr getArgument(int i) { result = this.(Call).getArgument(i) }
/**
* Gets the number of actual parameters in this call; use
* `getArgument(i)` with `i` between `0` and `result - 1` to
* retrieve actuals.
*/
int getNumberOfArguments() { result = this.(Call).getNumberOfArguments() }
/**
* Gets the index at which the format string occurs in the argument list.
*/
int getFormatParameterIndex() { result = this.getTarget().getFormatParameterIndex() }
/**
* Gets the format expression used in this call.
*/
Expr getFormat() { result = this.getArgument(this.getFormatParameterIndex()) }
/**
* Gets the nth argument to the format (including width and precision arguments).
*/
Expr getFormatArgument(int n) {
exists(int i |
result = this.getArgument(i) and
n >= 0 and
n = i - this.getTarget().getFirstFormatArgumentIndex()
)
}
/**
* Gets the argument corresponding to the nth conversion specifier.
*/
Expr getConversionArgument(int n) {
exists(FormatLiteral fl |
fl = this.getFormat() and
(
result = this.getFormatArgument(fl.getParameterFieldValue(n))
or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 2)) and
not exists(fl.getParameterFieldValue(n))
)
)
}
/**
* Gets the argument corresponding to the nth conversion specifier's
* minimum field width (has no result if that conversion specifier has
* an explicit minimum field width).
*/
Expr getMinFieldWidthArgument(int n) {
exists(FormatLiteral fl |
fl = this.getFormat() and
(
result = this.getFormatArgument(fl.getMinFieldWidthParameterFieldValue(n))
or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
not exists(fl.getMinFieldWidthParameterFieldValue(n))
)
)
}
/**
* Gets the argument corresponding to the nth conversion specifier's
* precision (has no result if that conversion specifier has an explicit
* precision).
*/
Expr getPrecisionArgument(int n) {
exists(FormatLiteral fl |
fl = this.getFormat() and
(
result = this.getFormatArgument(fl.getPrecisionParameterFieldValue(n))
or
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
not exists(fl.getPrecisionParameterFieldValue(n))
)
)
}
/**
* Gets the number of arguments to this call that are parameters to the
* format string.
*/
int getNumFormatArgument() {
result = count(this.getFormatArgument(_)) and
// format arguments must be known
exists(this.getTarget().getFirstFormatArgumentIndex())
}
/**
* Gets the argument, if any, to which the output is written. If `isStream` is
* `true`, the output argument is a stream (that is, this call behaves like
* `fprintf`). If `isStream` is `false`, the output argument is a buffer (that
* is, this call behaves like `sprintf`)
*/
Expr getOutputArgument(boolean isStream) {
result =
this.(Call)
.getArgument(this.(Call)
.getTarget()
.(FormattingFunction)
.getOutputParameterIndex(isStream))
}
}
/**
* Gets the number of digits required to represent the integer represented by `f`.
*
* `f` is assumed to be nonnegative.
*/
bindingset[f]
private int lengthInBase10(float f) {
f = 0 and result = 1
or
result = f.log10().floor() + 1
}
pragma[nomagic]
private predicate isPointerTypeWithBase(Type base, PointerType pt) { base = pt.getBaseType() }
bindingset[expr]
private BufferWriteEstimationReason getEstimationReasonForIntegralExpression(Expr expr) {
// we consider the range analysis non trivial if it
// * constrained non-trivially both sides of a signed value, or
// * constrained non-trivially the positive side of an unsigned value
// expr should already be given as getFullyConverted
if
upperBound(expr) < exprMaxVal(expr) and
(exprMinVal(expr) >= 0 or lowerBound(expr) > exprMinVal(expr))
then
// next we check whether the estimate may have been widened
if upperBoundMayBeWidened(expr)
then result = TWidenedValueFlowAnalysis()
else result = TValueFlowAnalysis()
else result = TTypeBoundsAnalysis()
}
/**
* Gets the number of hex digits required to represent the integer represented by `f`.
*
* `f` is assumed to be nonnegative.
*/
bindingset[f]
private int lengthInBase16(float f) {
f = 0 and result = 1
or
result = (f.log2() / 4.0).floor() + 1
}
/**
* A class to represent format strings that occur as arguments to invocations of formatting functions.
*/
class FormatLiteral extends Literal instanceof StringLiteral {
FormatLiteral() { exists(FormattingFunctionCall ffc | ffc.getFormat() = this) }
/**
* Gets the function call where this format string is used.
*/
FormattingFunctionCall getUse() { result.getFormat() = this }
/**
* Gets the default character type expected for `%s` by this format literal. Typically
* `char` or `wchar_t`.
*/
Type getDefaultCharType() { result = this.getUse().getTarget().getDefaultCharType() }
/**
* Gets the non-default character type expected for `%S` by this format literal. Typically
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
* which is correct for a particular function.
*/
Type getNonDefaultCharType() { result = this.getUse().getTarget().getNonDefaultCharType() }
/**
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
* snapshots there may be multiple results where we can't tell which is correct for a
* particular function.
*/
Type getWideCharType() { result = this.getUse().getTarget().getWideCharType() }
/**
* Holds if this `FormatLiteral` is in a context that supports
* Microsoft rules and extensions.
*/
predicate isMicrosoft() { anyFileCompiledAsMicrosoft() }
/**
* Gets the format string, with '%%' and '%@' replaced by '_' (to avoid processing
* them as format specifiers).
*/
string getFormat() { result = this.getValue().replaceAll("%%", "_").replaceAll("%@", "_") }
/**
* Gets the number of conversion specifiers (not counting `%%`)
*/
int getNumConvSpec() { result = count(this.getFormat().indexOf("%")) }
/**
* Gets the position in the string at which the nth conversion specifier
* starts.
*/
int getConvSpecOffset(int n) { result = this.getFormat().indexOf("%", n, 0) }
/**
* Gets the nth conversion specifier string.
*/
private string getConvSpecString(int n) {
n >= 0 and result = "%" + this.getFormat().splitAt("%", n + 1)
}
/*
* Each of these predicates gets a regular expressions to match each individual
* parts of a conversion specifier.
*/
private string getParameterFieldRegexp() {
// the parameter field is a posix extension, for example `%5$i` uses the fifth
// parameter as an integer, regardless of the position of this substring in the
// format string.
result = "(?:[1-9][0-9]*\\$)?"
}
private string getFlagRegexp() {
if this.isMicrosoft() then result = "[-+ #0']*" else result = "[-+ #0'I]*"
}
private string getFieldWidthRegexp() { result = "(?:[1-9][0-9]*|\\*|\\*[0-9]+\\$)?" }
private string getPrecRegexp() { result = "(?:\\.(?:[0-9]*|\\*|\\*[0-9]+\\$))?" }
private string getLengthRegexp() {
if this.isMicrosoft()
then result = "(?:hh?|ll?|L|q|j|z|t|w|I32|I64|I)?"
else result = "(?:hh?|ll?|L|q|j|z|Z|t)?"
}
private string getConvCharRegexp() {
if this.isMicrosoft()
then result = "[aAcCdeEfFgGimnopsSuxXZ@]"
else result = "[aAcCdeEfFgGimnopsSuxX@]"
}
/**
* Gets a regular expression used for matching a whole conversion specifier.
*/
string getConvSpecRegexp() {
// capture groups: 1 - entire conversion spec, including "%"
// 2 - parameters
// 3 - flags
// 4 - minimum field width
// 5 - precision
// 6 - length
// 7 - conversion character
// NB: this matches "%%" with conversion character "%"
result =
"(?s)(\\%(" + this.getParameterFieldRegexp() + ")(" + this.getFlagRegexp() + ")(" +
this.getFieldWidthRegexp() + ")(" + this.getPrecRegexp() + ")(" + this.getLengthRegexp() +
")(" + this.getConvCharRegexp() + ")" + "|\\%\\%).*"
}
/**
* Holds if the arguments are a parsing of a conversion specifier to this
* format string, where `n` is which conversion specifier to parse, `spec` is
* the whole conversion specifier, `params` is the argument to be converted
* in case it's not positional, `flags` contains additional format flags,
* `width` is the maximum width option of this input, `len` is the length
* flag of this input, and `conv` is the conversion character of this input.
*
* Each parameter is the empty string if no value is given by the conversion
* specifier.
*/
predicate parseConvSpec(
int n, string spec, string params, string flags, string width, string prec, string len,
string conv
) {
exists(string rst, string regexp |
rst = this.getConvSpecString(n) and
regexp = this.getConvSpecRegexp() and
(
spec = rst.regexpCapture(regexp, 1) and
params = rst.regexpCapture(regexp, 2) and
flags = rst.regexpCapture(regexp, 3) and
width = rst.regexpCapture(regexp, 4) and
prec = rst.regexpCapture(regexp, 5) and
len = rst.regexpCapture(regexp, 6) and
conv = rst.regexpCapture(regexp, 7)
or
spec = rst.regexpCapture(regexp, 1) and
not exists(rst.regexpCapture(regexp, 2)) and
params = "" and
flags = "" and
width = "" and
prec = "" and
len = "" and
conv = "%"
)
)
}
/**
* Gets the nth conversion specifier (including the initial `%`).
*/
string getConvSpec(int n) {
exists(string rst, string regexp |
rst = this.getConvSpecString(n) and
regexp = this.getConvSpecRegexp() and
result = rst.regexpCapture(regexp, 1)
)
}
/**
* Gets the parameter field of the nth conversion specifier (for example, `1$`).
*/
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) }
/**
* Gets the parameter field of the nth conversion specifier (if it has one) as a
* zero-based number.
*/
int getParameterFieldValue(int n) {
result = this.getParameterField(n).regexpCapture("([0-9]*)\\$", 1).toInt() - 1
}
/**
* Gets the flags of the nth conversion specifier.
*/
string getFlags(int n) { this.parseConvSpec(n, _, _, result, _, _, _, _) }
/**
* Holds if the nth conversion specifier has alternate flag ("#").
*/
predicate hasAlternateFlag(int n) { this.getFlags(n).matches("%#%") }
/**
* Holds if the nth conversion specifier has zero padding flag ("0").
*/
predicate isZeroPadded(int n) { this.getFlags(n).matches("%0%") and not this.isLeftAdjusted(n) }
/**
* Holds if the nth conversion specifier has flag left adjustment flag
* ("-"). Note that this overrides the zero padding flag.
*/
predicate isLeftAdjusted(int n) { this.getFlags(n).matches("%-%") }
/**
* Holds if the nth conversion specifier has the blank flag (" ").
*/
predicate hasBlank(int n) { this.getFlags(n).matches("% %") }
/**
* Holds if the nth conversion specifier has the explicit sign flag ("+").
*/
predicate hasSign(int n) { this.getFlags(n).matches("%+%") }
/**
* Holds if the nth conversion specifier has the thousands grouping flag ("'").
*/
predicate hasThousandsGrouping(int n) { this.getFlags(n).matches("%'%") }
/**
* Holds if the nth conversion specifier has the alternative digits flag ("I").
*/
predicate hasAlternativeDigits(int n) { this.getFlags(n).matches("%I%") }
/**
* Gets the minimum field width of the nth conversion specifier
* (empty string if none is given).
*/
string getMinFieldWidthOpt(int n) { this.parseConvSpec(n, _, _, _, result, _, _, _) }
/**
* Holds if the nth conversion specifier has a minimum field width.
*/
predicate hasMinFieldWidth(int n) { this.getMinFieldWidthOpt(n) != "" }
/**
* Holds if the nth conversion specifier has an explicitly given minimum
* field width.
*/
predicate hasExplicitMinFieldWidth(int n) { this.getMinFieldWidthOpt(n).regexpMatch("[0-9]+") }
/**
* Holds if the nth conversion specifier has an implicitly given minimum
* field width (either "*" or "*i$" for some number i).
*/
predicate hasImplicitMinFieldWidth(int n) { this.getMinFieldWidthOpt(n).regexpMatch("\\*.*") }
/**
* Gets the minimum field width of the nth conversion specifier.
*/
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() }
/**
* Gets the zero-based parameter number of the minimum field width of the nth
* conversion specifier, if it is implicit and uses a parameter field (such as `*1$`).
*/
int getMinFieldWidthParameterFieldValue(int n) {
result = this.getMinFieldWidthOpt(n).regexpCapture("\\*([0-9]*)\\$", 1).toInt() - 1
}
/**
* Gets the precision of the nth conversion specifier (empty string if none is given).
*/
string getPrecisionOpt(int n) { this.parseConvSpec(n, _, _, _, _, result, _, _) }
/**
* Holds if the nth conversion specifier has a precision.
*/
predicate hasPrecision(int n) { this.getPrecisionOpt(n) != "" }
/**
* Holds if the nth conversion specifier has an explicitly given precision.
*/
predicate hasExplicitPrecision(int n) { this.getPrecisionOpt(n).regexpMatch("\\.[0-9]*") }
/**
* Holds if the nth conversion specifier has an implicitly given precision
* (either "*" or "*i$" for some number i).
*/
predicate hasImplicitPrecision(int n) { this.getPrecisionOpt(n).regexpMatch("\\.\\*.*") }
/**
* Gets the precision of the nth conversion specifier.
*/
int getPrecision(int n) {
if this.getPrecisionOpt(n) = "."
then result = 0
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt()
}
/**
* Gets the zero-based parameter number of the precision of the nth conversion
* specifier, if it is implicit and uses a parameter field (such as `*1$`).
*/
int getPrecisionParameterFieldValue(int n) {
result = this.getPrecisionOpt(n).regexpCapture("\\.\\*([0-9]*)\\$", 1).toInt() - 1
}
/**
* Gets the length flag of the nth conversion specifier.
*/
string getLength(int n) { this.parseConvSpec(n, _, _, _, _, _, result, _) }
/**
* Gets the conversion character of the nth conversion specifier.
*/
string getConversionChar(int n) { this.parseConvSpec(n, _, _, _, _, _, _, result) }
/**
* Gets the size of pointers in the target this formatting function is
* compiled for.
*/
private int targetBitSize() { result = this.getFullyConverted().getType().getSize() }
private LongType getLongType() {
this.targetBitSize() = 4 and result.getSize() = min(LongType l | | l.getSize())
or
this.targetBitSize() = 8 and result.getSize() = max(LongType l | | l.getSize())
or
this.targetBitSize() != 4 and
this.targetBitSize() != 8
}
private Intmax_t getIntmax_t() {
this.targetBitSize() = 4 and result.getSize() = min(Intmax_t l | | l.getSize())
or
this.targetBitSize() = 8 and result.getSize() = max(Intmax_t l | | l.getSize())
or
this.targetBitSize() != 4 and
this.targetBitSize() != 8
}
private Size_t getSize_t() {
this.targetBitSize() = 4 and result.getSize() = min(Size_t l | | l.getSize())
or
this.targetBitSize() = 8 and result.getSize() = max(Size_t l | | l.getSize())
or
this.targetBitSize() != 4 and
this.targetBitSize() != 8
}
private Ssize_t getSsize_t() {
this.targetBitSize() = 4 and result.getSize() = min(Ssize_t l | | l.getSize())
or
this.targetBitSize() = 8 and result.getSize() = max(Ssize_t l | | l.getSize())
or
this.targetBitSize() != 4 and
this.targetBitSize() != 8
}
private Ptrdiff_t getPtrdiff_t() {
this.targetBitSize() = 4 and result.getSize() = min(Ptrdiff_t l | | l.getSize())
or
this.targetBitSize() = 8 and result.getSize() = max(Ptrdiff_t l | | l.getSize())
or
this.targetBitSize() != 4 and
this.targetBitSize() != 8
}
/**
* Gets the family of integral types required by the nth conversion
* specifier's length flag.
*/
Type getIntegralConversion(int n) {
exists(string len |
len = this.getLength(n) and
(
len = "hh" and result instanceof IntType
or
len = "h" and result instanceof IntType
or
len = "l" and result = this.getLongType()
or
len = ["ll", "L", "q"] and
result instanceof LongLongType
or
len = "j" and result = this.getIntmax_t()
or
len = ["z", "Z"] and
(result = this.getSize_t() or result = this.getSsize_t())
or
len = "t" and result = this.getPtrdiff_t()
or
len = "I" and
(result = this.getSize_t() or result = this.getPtrdiff_t())
or
len = "I32" and
exists(MicrosoftInt32Type t | t.getUnsigned() = result.(IntegralType).getUnsigned())
or
len = "I64" and
exists(MicrosoftInt64Type t | t.getUnsigned() = result.(IntegralType).getUnsigned())
or
len = "" and result instanceof IntType
)
)
}
/**
* Gets the family of integral types output / displayed by the nth
* conversion specifier's length flag.
*/
Type getIntegralDisplayType(int n) {
exists(string len |
len = this.getLength(n) and
(
len = "hh" and result instanceof CharType
or
len = "h" and result instanceof ShortType
or
len = "l" and result = this.getLongType()
or
len = ["ll", "L", "q"] and
result instanceof LongLongType
or
len = "j" and result = this.getIntmax_t()
or
len = ["z", "Z"] and
(result = this.getSize_t() or result = this.getSsize_t())
or
len = "t" and result = this.getPtrdiff_t()
or
len = "I" and
(result = this.getSize_t() or result = this.getPtrdiff_t())
or
len = "I32" and
exists(MicrosoftInt32Type t | t.getUnsigned() = result.(IntegralType).getUnsigned())
or
len = "I64" and
exists(MicrosoftInt64Type t | t.getUnsigned() = result.(IntegralType).getUnsigned())
or
len = "" and result instanceof IntType
)
)
}
/**
* Gets the family of floating point types required by the nth conversion
* specifier's length flag.
*/
FloatingPointType getFloatingPointConversion(int n) {
exists(string len |
len = this.getLength(n) and
if len = ["L", "ll"] then result instanceof LongDoubleType else result instanceof DoubleType
)
}
/**
* Gets the family of pointer types required by the nth conversion
* specifier's length flag.
*/
PointerType getStorePointerConversion(int n) {
exists(IntegralType base |
exists(string len | len = this.getLength(n) |
len = "hh" and base instanceof CharType
or
len = "h" and base instanceof ShortType
or
len = "l" and base = this.getLongType()
or
len = ["ll", "L"] and
base instanceof LongLongType
or
len = "q" and base instanceof LongLongType
) and
base.isSigned() and
base = result.getBaseType()
)
}
/**
* Gets the argument type required by the nth conversion specifier.
*/
Type getConversionType(int n) {
result = this.getConversionType1(n) or
result = this.getConversionType1b(n) or
result = this.getConversionType2(n) or
result = this.getConversionType3(n) or
result = this.getConversionType4(n) or
result = this.getConversionType6(n) or
result = this.getConversionType7(n) or
result = this.getConversionType8(n) or
result = this.getConversionType9(n) or
result = this.getConversionType10(n)
}
private Type getConversionType1(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = ["d", "i"] and
result = this.getIntegralConversion(n) and
not result.getUnderlyingType().(IntegralType).isExplicitlySigned() and
not result.getUnderlyingType().(IntegralType).isExplicitlyUnsigned()
)
}
/**
* Gets the char type required by the `n`th conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char` for `printf`, `char` or `wchar_t` for `wprintf`).
* - the `%C` format character reverses wideness.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/
private Type getConversionType1b(int n) {
exists(string len, string conv |
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
(
conv = ["c", "C"] and
len = "h" and
result instanceof PlainCharType
or
conv = ["c", "C"] and
len = ["l", "w"] and
result = this.getWideCharType()
or
conv = "c" and
(len != "l" and len != "w" and len != "h") and
result = this.getDefaultCharType()
or
conv = "C" and
(len != "l" and len != "w" and len != "h") and
result = this.getNonDefaultCharType()
)
)
}
private Type getConversionType2(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = ["o", "u", "x", "X"] and
result = this.getIntegralConversion(n) and
result.getUnderlyingType().(IntegralType).isUnsigned()
)
}
private Type getConversionType3(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = ["a", "A", "e", "E", "f", "F", "g", "G"] and result = this.getFloatingPointConversion(n)
)
}
/**
* Gets the string type required by the `n`th conversion specifier.
* - in the base case this is the default for the formatting function
* (e.g. `char *` for `printf`, `char *` or `wchar_t *` for `wprintf`).
* - the `%S` format character reverses wideness on some platforms.
* - the size prefixes 'l'/'w' and 'h' override the type character
* to wide or single-byte characters respectively.
*/
private Type getConversionType4(int n) {
exists(string len, string conv |
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
(
conv = ["s", "S"] and
len = "h" and
isPointerTypeWithBase(any(PlainCharType plainCharType), result)
or
conv = ["s", "S"] and
len = ["l", "w"] and
isPointerTypeWithBase(this.getWideCharType(), result)
or
conv = "s" and
(len != "l" and len != "w" and len != "h") and
isPointerTypeWithBase(this.getDefaultCharType(), result)
or
conv = "S" and
(len != "l" and len != "w" and len != "h") and
isPointerTypeWithBase(this.getNonDefaultCharType(), result)
)
)
}
private Type getConversionType6(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = "p" and result instanceof VoidPointerType
)
}
private Type getConversionType7(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = "n" and result = this.getStorePointerConversion(n)
)
}
private IntPointerType getConversionType8(int n) {
exists(string cnv | cnv = this.getConversionChar(n) |
cnv = "n" and
not exists(this.getStorePointerConversion(n)) and
result.getBaseType().(IntType).isSigned() and
not result.getBaseType().(IntType).isExplicitlySigned()
)
}
private Type getConversionType9(int n) {
this.getConversionChar(n) = "Z" and
this.getLength(n) = ["l", "w"] and
exists(Type t |
t.getName() = "UNICODE_STRING" and
result.(PointerType).getBaseType() = t
)
}
private Type getConversionType10(int n) {
this.getConversionChar(n) = "Z" and