mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	Ensure that all code paths in strbuf_addv() call va_end() on the ap_saved copy that was made. Fixes the following coverity complaint: Error: VARARGS (CWE-237): [#def683] tools/perf/util/strbuf.c:106: missing_va_end: va_end was not called for "ap_saved". Signed-off-by: Sanskriti Sharma <sansharm@redhat.com> Reviewed-by: Jiri Olsa <jolsa@kernel.org> Cc: Joe Lawrence <joe.lawrence@redhat.com> Link: http://lkml.kernel.org/r/1538490554-8161-2-git-send-email-sansharm@redhat.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
		
			
				
	
	
		
			165 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0
 | 
						|
#include "debug.h"
 | 
						|
#include "util.h"
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
/*
 | 
						|
 * Used as the default ->buf value, so that people can always assume
 | 
						|
 * buf is non NULL and ->buf is NUL terminated even for a freshly
 | 
						|
 * initialized strbuf.
 | 
						|
 */
 | 
						|
char strbuf_slopbuf[1];
 | 
						|
 | 
						|
int strbuf_init(struct strbuf *sb, ssize_t hint)
 | 
						|
{
 | 
						|
	sb->alloc = sb->len = 0;
 | 
						|
	sb->buf = strbuf_slopbuf;
 | 
						|
	if (hint)
 | 
						|
		return strbuf_grow(sb, hint);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void strbuf_release(struct strbuf *sb)
 | 
						|
{
 | 
						|
	if (sb->alloc) {
 | 
						|
		zfree(&sb->buf);
 | 
						|
		strbuf_init(sb, 0);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
char *strbuf_detach(struct strbuf *sb, size_t *sz)
 | 
						|
{
 | 
						|
	char *res = sb->alloc ? sb->buf : NULL;
 | 
						|
	if (sz)
 | 
						|
		*sz = sb->len;
 | 
						|
	strbuf_init(sb, 0);
 | 
						|
	return res;
 | 
						|
}
 | 
						|
 | 
						|
int strbuf_grow(struct strbuf *sb, size_t extra)
 | 
						|
{
 | 
						|
	char *buf;
 | 
						|
	size_t nr = sb->len + extra + 1;
 | 
						|
 | 
						|
	if (nr < sb->alloc)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (nr <= sb->len)
 | 
						|
		return -E2BIG;
 | 
						|
 | 
						|
	if (alloc_nr(sb->alloc) > nr)
 | 
						|
		nr = alloc_nr(sb->alloc);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Note that sb->buf == strbuf_slopbuf if sb->alloc == 0, and it is
 | 
						|
	 * a static variable. Thus we have to avoid passing it to realloc.
 | 
						|
	 */
 | 
						|
	buf = realloc(sb->alloc ? sb->buf : NULL, nr * sizeof(*buf));
 | 
						|
	if (!buf)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	sb->buf = buf;
 | 
						|
	sb->alloc = nr;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int strbuf_addch(struct strbuf *sb, int c)
 | 
						|
{
 | 
						|
	int ret = strbuf_grow(sb, 1);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	sb->buf[sb->len++] = c;
 | 
						|
	sb->buf[sb->len] = '\0';
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int strbuf_add(struct strbuf *sb, const void *data, size_t len)
 | 
						|
{
 | 
						|
	int ret = strbuf_grow(sb, len);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	memcpy(sb->buf + sb->len, data, len);
 | 
						|
	return strbuf_setlen(sb, sb->len + len);
 | 
						|
}
 | 
						|
 | 
						|
static int strbuf_addv(struct strbuf *sb, const char *fmt, va_list ap)
 | 
						|
{
 | 
						|
	int len, ret;
 | 
						|
	va_list ap_saved;
 | 
						|
 | 
						|
	if (!strbuf_avail(sb)) {
 | 
						|
		ret = strbuf_grow(sb, 64);
 | 
						|
		if (ret)
 | 
						|
			return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	va_copy(ap_saved, ap);
 | 
						|
	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap);
 | 
						|
	if (len < 0) {
 | 
						|
		va_end(ap_saved);
 | 
						|
		return len;
 | 
						|
	}
 | 
						|
	if (len > strbuf_avail(sb)) {
 | 
						|
		ret = strbuf_grow(sb, len);
 | 
						|
		if (ret) {
 | 
						|
			va_end(ap_saved);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap_saved);
 | 
						|
		va_end(ap_saved);
 | 
						|
		if (len > strbuf_avail(sb)) {
 | 
						|
			pr_debug("this should not happen, your vsnprintf is broken");
 | 
						|
			va_end(ap_saved);
 | 
						|
			return -EINVAL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	va_end(ap_saved);
 | 
						|
	return strbuf_setlen(sb, sb->len + len);
 | 
						|
}
 | 
						|
 | 
						|
int strbuf_addf(struct strbuf *sb, const char *fmt, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	va_start(ap, fmt);
 | 
						|
	ret = strbuf_addv(sb, fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
ssize_t strbuf_read(struct strbuf *sb, int fd, ssize_t hint)
 | 
						|
{
 | 
						|
	size_t oldlen = sb->len;
 | 
						|
	size_t oldalloc = sb->alloc;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = strbuf_grow(sb, hint ? hint : 8192);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		ssize_t cnt;
 | 
						|
 | 
						|
		cnt = read(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
 | 
						|
		if (cnt < 0) {
 | 
						|
			if (oldalloc == 0)
 | 
						|
				strbuf_release(sb);
 | 
						|
			else
 | 
						|
				strbuf_setlen(sb, oldlen);
 | 
						|
			return cnt;
 | 
						|
		}
 | 
						|
		if (!cnt)
 | 
						|
			break;
 | 
						|
		sb->len += cnt;
 | 
						|
		ret = strbuf_grow(sb, 8192);
 | 
						|
		if (ret)
 | 
						|
			return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	sb->buf[sb->len] = '\0';
 | 
						|
	return sb->len - oldlen;
 | 
						|
}
 |