Skip to content

Commit 37f1600

Browse files
daviesrobpd3
authored andcommitted
Ensure clean compilation with -Wformat -Wformat=2
Companion to samtools/htslib#1816, which among other things adds printf format checking attributes to bcf_hdr_printf(). This fixes a few places that could trigger warnings in bcftools as a result of that change. It also adds checking for some functions internal to bcftools so `clang -Wformat -Wformat=2` works and fixes a couple of bugs that were found as a result. Bugs fixed: * In open_file() a user-controlled string was passed as the format to mkdir_p(), which could lead to information leakage or possibly arbitrary writes via a crafted file path name. * Use of wrong type for position in vcfsort sort_blocks() error report. * Printing the wrong variable in vcfsort do_partial_merge() error report. The -Wformat=2 option disallows non-literals in printf-like functions, which can be a little difficult to work around. Here most cases were fairly easy to sort out, apart from hdr_append() in plugins/fill-tags.c. For that the chosen solution was to convert the function into a macro. While not particularly nice it does allow all of the format strings passed in to be validated properly. Adds Wformat to clang CI build to catch any future regressions.
1 parent 39a1407 commit 37f1600

File tree

13 files changed

+53
-39
lines changed

13 files changed

+53
-39
lines changed

.cirrus.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ ubuntu_task:
8484

8585
environment:
8686
CC: clang
87+
CFLAGS: -g -O2 -Werror -Wall -Wformat -Wformat=2
8788
LC_ALL: C
8889
CIRRUS_CLONE_DEPTH: 1
8990
HTSDIR: ./htslib

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,15 @@ vcfconvert.o: vcfconvert.c $(htslib_faidx_h) $(htslib_vcf_h) $(htslib_bgzf_h) $(
251251
vcffilter.o: vcffilter.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(bcftools_h) $(filter_h) rbuf.h regidx.h
252252
vcfgtcheck.o: vcfgtcheck.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_kbitset_h) $(htslib_hts_os_h) $(htslib_bgzf_h) $(bcftools_h) extsort.h filter.h
253253
vcfindex.o: vcfindex.c $(htslib_vcf_h) $(htslib_tbx_h) $(htslib_kstring_h) $(htslib_bgzf_h) $(bcftools_h)
254-
vcfisec.o: vcfisec.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_hts_os_h) $(bcftools_h) $(filter_h)
254+
vcfisec.o: vcfisec.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_hts_os_h) $(htslib_hts_defs_h) $(bcftools_h) $(filter_h)
255255
vcfmerge.o: vcfmerge.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_faidx_h) $(htslib_kbitset_h) $(htslib_hts_endian_h) $(bcftools_h) regidx.h vcmp.h $(htslib_khash_h) $(htslib_kbitset_h)
256256
vcfnorm.o: vcfnorm.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_faidx_h) $(htslib_khash_str2int_h) $(bcftools_h) rbuf.h abuf.h gff.h regidx.h
257257
vcfquery.o: vcfquery.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_khash_str2int_h) $(htslib_vcfutils_h) $(bcftools_h) $(filter_h) $(convert_h) $(smpl_ilist_h)
258258
vcfroh.o: vcfroh.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_kstring_h) $(htslib_kseq_h) $(htslib_bgzf_h) $(bcftools_h) HMM.h $(smpl_ilist_h) $(filter_h)
259-
vcfcnv.o: vcfcnv.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_kstring_h) $(htslib_kfunc_h) $(htslib_khash_str2int_h) $(bcftools_h) HMM.h rbuf.h
259+
vcfcnv.o: vcfcnv.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_kstring_h) $(htslib_kfunc_h) $(htslib_khash_str2int_h) $(htslib_hts_defs_h) $(bcftools_h) HMM.h rbuf.h
260260
vcfhead.o: vcfhead.c $(htslib_kstring_h) $(htslib_vcf_h) $(bcftools_h)
261-
vcfsom.o: vcfsom.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_hts_os_h) $(bcftools_h)
262-
vcfsort.o: vcfsort.c $(htslib_vcf_h) $(htslib_kstring_h) $(htslib_hts_os_h) $(htslib_bgzf_h) kheap.h $(bcftools_h)
261+
vcfsom.o: vcfsom.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_hts_os_h) $(htslib_hts_defs_h) $(bcftools_h)
262+
vcfsort.o: vcfsort.c $(htslib_vcf_h) $(htslib_kstring_h) $(htslib_hts_os_h) $(htslib_hts_defs_h) $(htslib_bgzf_h) kheap.h $(bcftools_h)
263263
vcfstats.o: vcfstats.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(htslib_faidx_h) $(bcftools_h) $(filter_h) bin.h dist.h
264264
vcfview.o: vcfview.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_vcfutils_h) $(bcftools_h) $(filter_h) $(htslib_khash_str2int_h) $(htslib_kbitset_h)
265265
reheader.o: reheader.c $(htslib_vcf_h) $(htslib_bgzf_h) $(htslib_tbx_h) $(htslib_kseq_h) $(htslib_thread_pool_h) $(htslib_faidx_h) $(htslib_khash_str2int_h) $(bcftools_h) $(khash_str2str_h)
@@ -276,7 +276,7 @@ mcall.o: mcall.c $(htslib_kfunc_h) $(htslib_khash_str2int_h) $(call_h) $(prob1_h
276276
prob1.o: prob1.c $(prob1_h)
277277
vcmp.o: vcmp.c $(htslib_hts_h) $(htslib_vcf_h) vcmp.h
278278
ploidy.o: ploidy.c $(htslib_khash_str2int_h) $(htslib_kseq_h) $(htslib_hts_h) $(bcftools_h) $(ploidy_h)
279-
polysomy.o: polysomy.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(bcftools_h) peakfit.h
279+
polysomy.o: polysomy.c $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) $(htslib_hts_defs_h) $(bcftools_h) peakfit.h
280280
peakfit.o: peakfit.c peakfit.h $(htslib_hts_h) $(htslib_kstring_h)
281281
bin.o: bin.c $(bcftools_h) bin.h
282282
dist.o: dist.c dist.h
@@ -326,7 +326,7 @@ test/test-rbuf.o: test/test-rbuf.c rbuf.h
326326
test/test-rbuf: test/test-rbuf.o
327327
$(CC) $(LDFLAGS) -o $@ $^ $(ALL_LIBS)
328328

329-
test/test-regidx.o: test/test-regidx.c $(htslib_kstring_h) $(htslib_hts_os_h) regidx.h
329+
test/test-regidx.o: test/test-regidx.c $(htslib_kstring_h) $(htslib_hts_os_h) $(htslib_hts_defs_h) regidx.h
330330

331331
test/test-regidx: test/test-regidx.o regidx.o | $(HTSLIB)
332332
$(CC) $(ALL_LDFLAGS) -o $@ $^ $(HTSLIB_LIB) -lpthread $(ALL_LIBS)

plugins/fill-tags.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,17 @@ int ftf_filter_expr(args_t *args, bcf1_t *rec, pop_t *pop, ftf_t *ftf)
342342
error("Error occurred while updating %s at %s:%"PRId64"\n", args->str.s,bcf_seqname(args->in_hdr,rec),(int64_t) rec->pos+1);
343343
return 0;
344344
}
345-
void hdr_append(args_t *args, char *fmt)
346-
{
347-
int i;
348-
for (i=0; i<args->npop; i++)
349-
bcf_hdr_printf(args->out_hdr, fmt, args->pop[i].suffix,*args->pop[i].name ? " in " : "",args->pop[i].name);
350-
}
345+
346+
// This is implemented as a macro so the compiler can properly validate the
347+
// printf format string.
348+
#define hdr_append(args, fmt) \
349+
do { \
350+
int i; \
351+
for (i=0; i<args->npop; i++) \
352+
bcf_hdr_printf(args->out_hdr, fmt, args->pop[i].suffix,*args->pop[i].name ? " in " : "",args->pop[i].name); \
353+
} while (0)
354+
355+
351356
int parse_func_pop(args_t *args, pop_t *pop, char *tag_expr, char *expr)
352357
{
353358
pop->nftf++;

plugins/scatter.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <unistd.h>
2929
#include <ctype.h>
3030
#include <htslib/synced_bcf_reader.h>
31+
#include <htslib/hts_defs.h>
3132
#include "bcftools.h"
3233
#include "htslib/khash_str2int.h"
3334
#include "regidx.h"
@@ -108,7 +109,7 @@ static const char *usage_text(void)
108109
"\n";
109110
}
110111

111-
void mkdir_p(const char *fmt, ...);
112+
void mkdir_p(const char *fmt, ...) HTS_FORMAT(HTS_PRINTF_FMT, 1, 2);
112113

113114
// most of this code was inspired by Petr Danecek's code in regidx.c
114115
#define MAX_COOR_0 REGIDX_MAX // CSI and hts_itr_query limit, 0-based

plugins/split-vep.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,15 +711,13 @@ static void parse_column_str(args_t *args)
711711
ann->idx = j = column[i];
712712
ann->field = strdup(args->field[j]);
713713
ann->tag = strdup(args->field[j]);
714-
args->kstr.l = 0;
715714
const char *type = "String";
716715
if ( ann->type==BCF_HT_REAL ) type = "Float";
717716
else if ( ann->type==BCF_HT_INT ) type = "Integer";
718717
else if ( ann->type==BCF_HT_FLAG ) type = "Flag";
719718
else if ( ann->type==BCF_HT_STR ) type = "String";
720719
else if ( ann->type==-1 ) type = get_column_type(args, args->field[j], &ann->type);
721-
ksprintf(&args->kstr,"##INFO=<ID=%%s,Number=.,Type=%s,Description=\"The %%s field from INFO/%%s\">",type);
722-
bcf_hdr_printf(args->hdr_out, args->kstr.s, ann->tag,ann->field,args->vep_tag);
720+
bcf_hdr_printf(args->hdr_out, "##INFO=<ID=%s,Number=.,Type=%s,Description=\"The %s field from INFO/%s\">", ann->tag,type,ann->field,args->vep_tag);
723721
if ( str.l ) kputc(',',&str);
724722
kputs(ann->tag,&str);
725723
}

plugins/split.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static const char *usage_text(void)
143143
"\n";
144144
}
145145

146-
void mkdir_p(const char *fmt, ...);
146+
void mkdir_p(const char *fmt, ...) HTS_FORMAT(HTS_PRINTF_FMT, 1, 2);
147147

148148
static char *create_unique_file_name(args_t *args, const char *template)
149149
{

polysomy.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <gsl/gsl_multifit_nlin.h>
3535
#include <htslib/vcf.h>
3636
#include <htslib/synced_bcf_reader.h>
37+
#include <htslib/hts_defs.h>
3738
#include "bcftools.h"
3839
#include "peakfit.h"
3940

@@ -62,7 +63,7 @@ typedef struct
6263
}
6364
args_t;
6465

65-
FILE *open_file(char **fname, const char *mode, const char *fmt, ...);
66+
FILE *open_file(char **fname, const char *mode, const char *fmt, ...) HTS_FORMAT(HTS_PRINTF_FMT, 3, 4);
6667

6768
static void init_dist(args_t *args, dist_t *dist, int verbose)
6869
{

test/test-regidx.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,32 @@
3333
#include <getopt.h>
3434
#include <htslib/kstring.h>
3535
#include <htslib/hts_os.h>
36+
#include <htslib/hts_defs.h>
3637
#include <time.h>
3738
#include "regidx.h"
3839

3940
static int verbose = 0;
4041

41-
void debug(const char *format, ...)
42+
void HTS_FORMAT(HTS_PRINTF_FMT, 1, 2)
43+
debug(const char *format, ...)
4244
{
4345
if ( verbose<2 ) return;
4446
va_list ap;
4547
va_start(ap, format);
4648
vfprintf(stderr, format, ap);
4749
va_end(ap);
4850
}
49-
void info(const char *format, ...)
51+
void HTS_FORMAT(HTS_PRINTF_FMT, 1, 2)
52+
info(const char *format, ...)
5053
{
5154
if ( verbose<1 ) return;
5255
va_list ap;
5356
va_start(ap, format);
5457
vfprintf(stderr, format, ap);
5558
va_end(ap);
5659
}
57-
void error(const char *format, ...)
60+
void HTS_FORMAT(HTS_PRINTF_FMT, 1, 2) HTS_NORETURN
61+
error(const char *format, ...)
5862
{
5963
va_list ap;
6064
va_start(ap, format);

vcfcnv.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <htslib/kstring.h>
4242
#include <htslib/kfunc.h>
4343
#include <htslib/khash_str2int.h>
44+
#include <htslib/hts_defs.h>
4445
#include "bcftools.h"
4546
#include "HMM.h"
4647
#include "rbuf.h"
@@ -105,7 +106,7 @@ typedef struct _args_t
105106
}
106107
args_t;
107108

108-
FILE *open_file(char **fname, const char *mode, const char *fmt, ...);
109+
FILE *open_file(char **fname, const char *mode, const char *fmt, ...) HTS_FORMAT(HTS_PRINTF_FMT, 3, 4);
109110

110111
static inline void hmm2cn_state(int nstates, int i, int *a, int *b)
111112
{

vcfisec.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ THE SOFTWARE. */
3434
#include <htslib/synced_bcf_reader.h>
3535
#include <htslib/vcfutils.h>
3636
#include <htslib/hts_os.h>
37+
#include <htslib/hts_defs.h>
3738
#include "bcftools.h"
3839
#include "filter.h"
3940

@@ -69,32 +70,33 @@ args_t;
6970
* mkdir_p() - create new directory for a file $fname
7071
* @fname: the file name to create the directory for, the part after last "/" is ignored
7172
*/
72-
void mkdir_p(const char *fmt, ...)
73+
void HTS_FORMAT(HTS_PRINTF_FMT, 1, 2)
74+
mkdir_p(const char *fmt, ...)
7375
{
7476
va_list ap;
7577
va_start(ap, fmt);
7678
int n = vsnprintf(NULL, 0, fmt, ap) + 2;
7779
va_end(ap);
7880

79-
char *path = (char*)malloc(n);
81+
char *tmp = (char*)malloc(n);
82+
if (!tmp) error("Couldn't allocate space for path: %s\n", strerror(errno));
8083
va_start(ap, fmt);
81-
vsnprintf(path, n, fmt, ap);
84+
vsnprintf(tmp, n, fmt, ap);
8285
va_end(ap);
8386

84-
char *tmp = strdup(path), *p = tmp+1;
87+
char *p = tmp+1;
8588
while (*p)
8689
{
8790
while (*p && *p!='/') p++;
8891
if ( !*p ) break;
8992
char ctmp = *p;
9093
*p = 0;
9194
int ret = mkdir(tmp,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
92-
if ( ret!=0 && errno!=EEXIST ) error("Error creating directory %s: %s\n", path,strerror(errno));
95+
if ( ret!=0 && errno!=EEXIST ) error("Error creating directory %s: %s\n", tmp,strerror(errno));
9396
*p = ctmp;
9497
while ( *p && *p=='/' ) p++;
9598
}
9699
free(tmp);
97-
free(path);
98100
}
99101

100102
/**
@@ -105,7 +107,8 @@ void mkdir_p(const char *fmt, ...)
105107
*
106108
* Returns open file descriptor or NULL if mode is NULL.
107109
*/
108-
FILE *open_file(char **fname, const char *mode, const char *fmt, ...)
110+
FILE * HTS_FORMAT(HTS_PRINTF_FMT, 3, 4)
111+
open_file(char **fname, const char *mode, const char *fmt, ...)
109112
{
110113
va_list ap;
111114
va_start(ap, fmt);
@@ -117,7 +120,7 @@ FILE *open_file(char **fname, const char *mode, const char *fmt, ...)
117120
vsnprintf(str, n, fmt, ap);
118121
va_end(ap);
119122

120-
mkdir_p(str);
123+
mkdir_p("%s", str);
121124
if ( !mode )
122125
{
123126
if ( !fname ) error("Uh: expected fname or mode\n");

0 commit comments

Comments
 (0)