Merge branch 'some-bounds-checks' into 'master'

Add some bounds checking to _reply functions

See merge request xorg/lib/libxcb!29
This commit is contained in:
Demi Marie Obenour 2025-07-24 16:29:54 -04:00
commit 0ff8c8c824
36 changed files with 632 additions and 197 deletions

View File

@ -15,7 +15,7 @@ AM_INIT_AUTOMAKE([foreign dist-xz])
AM_PATH_PYTHON([3.0])
# Set common system defines for POSIX extensions, such as _GNU_SOURCE
# Must be called before any macros that run the compiler (like AC_PROG_LIBTOOL)
# Must be called before any macros that run the compiler (like LT_INIT)
# to avoid autoconf errors.
AC_USE_SYSTEM_EXTENSIONS
AC_SYS_LARGEFILE
@ -27,6 +27,8 @@ LT_INIT([win32-dll])
# Require xorg-macros minimum of 1.18 - Initial version
m4_ifndef([XORG_MACROS_VERSION],
[m4_fatal([must install xorg-macros 1.18 or later before running autoconf/autogen])])
XORG_TESTSET_CFLAG([XCB_CFLAGS], [-fno-strict-aliasing])
AC_SUBST([XCB_CFLAGS])
XORG_MACROS_VERSION(1.18)
XORG_DEFAULT_OPTIONS
XORG_ENABLE_DEVEL_DOCS
@ -248,6 +250,23 @@ XCB_EXTENSION(XTest, yes)
XCB_EXTENSION(Xv, yes)
XCB_EXTENSION(XvMC, yes)
AC_MSG_CHECKING([[if __attribute__((may_alias)) is supported]])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
typedef struct x { int a; } __attribute__((may_alias)) x;
]])],[
AC_DEFINE([[XCB_MAY_ALIAS]], [[__attribute__((may_alias))]], [Define if __attribute__((may_alias)) works])
AC_MSG_RESULT([[yes]])
],[
AC_MSG_RESULT([[no]])
AC_MSG_CHECKING([[if -fno-strict-aliasing works]])[
case $XCB_CFLAGS in
*-fno-strict-aliasing*)]
AC_MSG_RESULT([[yes]])
AC_MSG_WARN([[may_alias not supported, programs must use -fno-strict-aliasing]]);;
*)
AC_MSG_RESULT([[no (flags are $XCB_CFLAGS)]])
AC_MSG_FAILURE([[No method of suppressing strict-aliasing found]]);;
[esac]])
AC_ARG_WITH(serverside-support, AS_HELP_STRING([--with-serverside-support], [Build with support for server-side usage of xcb. This is still EXPERIMENTAL! ABI/API may change! (default: no)]), [XCB_SERVERSIDE_SUPPORT=$withval], [XCB_SERVERSIDE_SUPPORT=no])
AM_CONDITIONAL(XCB_SERVERSIDE_SUPPORT, test "x$XCB_SERVERSIDE_SUPPORT" = "xyes")

View File

@ -4,7 +4,7 @@ EXTSOURCES = xproto.c \
bigreq.c \
xc_misc.c
AM_CFLAGS = $(BASE_CFLAGS) $(NEEDED_CFLAGS) $(XDMCP_CFLAGS)
AM_CFLAGS = $(BASE_CFLAGS) $(NEEDED_CFLAGS) $(XDMCP_CFLAGS) $(XCB_CFLAGS)
libxcb_la_LIBADD = $(NEEDED_LIBS) $(XDMCP_LIBS)
libxcb_la_SOURCES = \
xcb_conn.c xcb_out.c xcb_in.c xcb_ext.c xcb_xid.c \
@ -249,7 +249,7 @@ nodist_libxcb_ge_la_SOURCES = ge.c ge.h
endif
EXTHEADERS=$(EXTSOURCES:.c=.h)
xcbinclude_HEADERS = xcb.h xcbext.h
xcbinclude_HEADERS = xcb.h xcbext.h config.h
if XCB_HAVE_WIN32
xcbinclude_HEADERS += xcb_windefs.h
endif

View File

@ -113,6 +113,7 @@ class PreCode(object):
def output_tempvars(self):
assert False
if self.redirect_code == None:
_c_wr_stringlist('', self.tempvars)
self.tempvars = []
@ -278,9 +279,10 @@ def c_open(self):
_h('#include "xcb.h"')
_c('#ifdef HAVE_CONFIG_H')
_c('#include "config.h"')
_hc('#include "config.h"')
_c('#endif')
_c('#include <stdlib.h>')
_c('#include <stdalign.h>')
_c('#include <string.h>')
_c('#include <assert.h>')
_c('#include <stddef.h> /* for offsetof() */')
@ -288,7 +290,6 @@ def c_open(self):
_c('#include "%s.h"', _ns.header)
_c('')
_c('#define ALIGNOF(type) offsetof(struct { char dummy; type member; }, member)')
if _ns.is_ext:
for (n, h) in self.direct_imports:
@ -308,6 +309,13 @@ def c_open(self):
_c('')
_c('xcb_extension_t %s = { "%s", 0 };', _ns.c_ext_global_name, _ns.ext_xname)
_c('static int xcb_checked_mul(unsigned long long x, unsigned long long y)')
_c('{')
_c(' int z;')
_c(' if (__builtin_mul_overflow(x, y, &z))')
_c(' return -1;')
_c(' return z;')
_c('}')
def c_close(self):
'''
@ -528,12 +536,14 @@ def _c_type_setup(self, name, postfix):
# special cases -> unserialize
if self.is_switch or self.c_var_followed_by_fixed_fields:
_c_serialize('unserialize', self)
_c_serialize('unserialize_checked', self)
if self.c_need_sizeof:
if self.c_sizeof_name not in finished_sizeof:
if not module.namespace.is_ext or self.name[:2] == module.namespace.prefix:
finished_sizeof.append(self.c_sizeof_name)
_c_serialize('sizeof', self)
_c_serialize('sizeof_checked', self)
# Functions for querying field properties
def _c_field_needs_list_accessor(field):
@ -791,8 +801,12 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
# 1. the parameter for the void * buffer
if 'serialize' == context:
params.append(('void', '**', buffer_var))
elif context in ('unserialize', 'unpack', 'sizeof'):
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
params.append(('const void', '*', buffer_var))
if context.endswith('checked'):
params.append(('const void', '*', 'xcb_end'))
else:
assert False
# 2. any expr fields that cannot be resolved within self and descendants
unresolved_fields = resolve_expr_fields(self)
@ -815,9 +829,9 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
# 4. aux argument
if 'serialize' == context:
add_param(params, ('const %s' % self.c_type, '*', aux_var))
elif 'unserialize' == context:
elif context.startswith('unserialize'):
add_param(params, ('%s' % self.c_type, '**', aux_var))
elif 'unpack' == context:
elif context.startswith('unpack'):
add_param(params, ('%s' % self.c_type, '*', aux_var))
# 5. switch contains all variable size fields as struct members
@ -849,7 +863,9 @@ def _c_serialize_helper_insert_padding(context, complex_type, code_lines, space,
code_lines.append('%s xcb_parts[xcb_parts_idx].iov_base = xcb_pad0;' % space)
code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = xcb_pad;' % space)
code_lines.append('%s xcb_parts_idx++;' % space)
elif context in ('unserialize', 'unpack', 'sizeof'):
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
if context.endswith('checked'):
_c_emit_bounds_check(code_lines, space + ' ', 'xcb_pad')
code_lines.append('%s xcb_tmp += xcb_pad;' % space)
code_lines.append('%s xcb_pad = 0;' % space)
@ -866,7 +882,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
code_lines, temp_vars,
space, prefix):
count = 0
switch_expr = _c_accessor_get_expr(self.expr, None)
switch_expr = _c_accessor_get_expr(self.expr, None, checked=context.endswith("checked"))
for b in self.bitcases:
len_expr = len(b.type.expr)
@ -878,7 +894,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
compare_operator = '&'
for n, expr in enumerate(b.type.expr):
bitcase_expr = _c_accessor_get_expr(expr, None)
bitcase_expr = _c_accessor_get_expr(expr, None, checked=context.endswith("checked"))
# only one <enumref> in the <bitcase>
if len_expr == 1:
code_lines.append(
@ -907,7 +923,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
# if 'serialize' == context:
# count += _c_serialize_helper_insert_padding(context, self, code_lines, space, False)
# elif context in ('unserialize', 'unpack', 'sizeof'):
# elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
# # padding
# code_lines.append('%s xcb_pad = -xcb_block_len & 3;' % space)
# code_lines.append('%s xcb_buffer_len += xcb_block_len + xcb_pad;' % space)
@ -946,13 +962,16 @@ def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, pr
if 'serialize' == context:
length = "%s(&%s, %s&%s%s)" % (field.type.c_serialize_name, c_switch_variable,
c_field_names, prefix_str, field.c_field_name)
elif context in ('unserialize', 'unpack'):
length = "%s(xcb_tmp, %s&%s%s)" % (field.type.c_unpack_name,
c_field_names, prefix_str, field.c_field_name)
elif 'sizeof' == context:
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked'):
length = "%s%s(xcb_tmp, %s%s&%s%s)" % (field.type.c_unpack_name, '_checked' if context.endswith('checked') else '',
'xcb_end, ' if context.endswith('checked') else '',
c_field_names, prefix_str, field.c_field_name)
elif context in ('sizeof', 'sizeof_checked'):
# remove trailing ", " from c_field_names because it will be used at end of arglist
my_c_field_names = c_field_names[:-2]
length = "%s(xcb_tmp, %s)" % (field.type.c_sizeof_name, my_c_field_names)
else:
assert False, 'bad context %r' % context
return length
@ -1011,10 +1030,10 @@ def _c_serialize_helper_list_field(context, self, field,
if expr.op == 'calculate_len':
list_length = field.type.expr.lenfield_name
else:
list_length = _c_accessor_get_expr(expr, field_mapping)
list_length = _c_accessor_get_expr(expr, field_mapping, checked=context.endswith("checked"))
# default: list with fixed size elements
length = '%s * sizeof(%s)' % (list_length, field.type.member.c_wiretype)
length = 'xcb_checked_mul(%s, sizeof(%s))' % (list_length, field.type.member.c_wiretype)
# list with variable-sized elements
if not field.type.member.fixed_size():
@ -1027,7 +1046,7 @@ def _c_serialize_helper_list_field(context, self, field,
#
length = ''
if context in ('unserialize', 'sizeof', 'unpack'):
if context in ('unserialize', 'unserialize_checked', 'sizeof', 'unpack', 'unpack_checked', 'sizeof_checked'):
int_i = ' unsigned int i;'
xcb_tmp_len = ' unsigned int xcb_tmp_len;'
if int_i not in temp_vars:
@ -1037,8 +1056,18 @@ def _c_serialize_helper_list_field(context, self, field,
# loop over all list elements and call sizeof repeatedly
# this should be a bit faster than using the iterators
code_lines.append("%s for(i=0; i<%s; i++) {" % (space, list_length))
code_lines.append("%s xcb_tmp_len = %s(xcb_tmp%s);" %
(space, field.type.c_sizeof_name, member_arg_str))
if context.endswith('checked'):
code_lines.append('%s if ((const char *)xcb_end < (const char *)xcb_tmp)' % (space,))
code_lines.append('%s abort();' % (space,))
code_lines.append("%s xcb_tmp_len = %s_checked(xcb_tmp, xcb_end%s);" %
(space, field.type.c_sizeof_name, member_arg_str))
code_lines.append("%s if (xcb_tmp_len < 0)" % (space,))
code_lines.append("%s return xcb_tmp_len;" % (space,))
code_lines.append('%s if ((size_t)((const char *)xcb_end - (const char *)xcb_tmp) < xcb_tmp_len)' % (space,))
code_lines.append('%s return -1;' % (space,))
else:
code_lines.append("%s xcb_tmp_len = %s(xcb_tmp%s);" %
(space, field.type.c_sizeof_name, member_arg_str))
code_lines.append("%s xcb_block_len += xcb_tmp_len;" % space)
code_lines.append("%s xcb_tmp += xcb_tmp_len;" % space)
code_lines.append("%s }" % space)
@ -1069,7 +1098,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
# default for simple cases: call sizeof()
length = "sizeof(%s)" % field.c_field_type
if context in ('unserialize', 'unpack', 'sizeof'):
if context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
# default: simple cast
value = ' %s = *(%s *)xcb_tmp;' % (abs_field_name, field.c_field_type)
@ -1077,7 +1106,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
if field.type.is_pad and field.type.nmemb > 1:
value = ''
for i in range(field.type.nmemb):
code_lines.append('%s %s[%d] = *(%s *)xcb_tmp;' %
code_lines.append('%s %s[%d] = *(const %s *)xcb_tmp;' %
(space, abs_field_name, i, field.c_field_type))
# total padding = sizeof(pad0) * nmemb
length += " * %d" % field.type.nmemb
@ -1097,10 +1126,10 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
# need to register a temporary variable for the expression in case we know its type
if field.type.c_type is None:
raise Exception("type for field '%s' (expression '%s') unkown" %
(field.field_name, _c_accessor_get_expr(field.type.expr)))
(field.field_name, _c_accessor_get_expr(field.type.expr, prefix, False)))
temp_vars.append(' %s xcb_expr_%s = %s;' % (field.type.c_type, _cpp(field.field_name),
_c_accessor_get_expr(field.type.expr, prefix)))
_c_accessor_get_expr(field.type.expr, prefix, False)))
value += "&xcb_expr_%s;" % _cpp(field.field_name)
elif field.type.is_pad:
@ -1128,16 +1157,16 @@ def _c_serialize_helper_fields_variable_size(context, self, field,
space, prefix):
prefix_str = _c_helper_fieldaccess_expr(prefix)
if context in ('unserialize', 'unpack', 'sizeof'):
if context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
value = ''
var_field_name = 'xcb_tmp'
# special case: intermixed fixed and variable size fields
if self.c_var_followed_by_fixed_fields and 'unserialize' == context:
if self.c_var_followed_by_fixed_fields and context.startswith('unserialize'):
value = ' %s = (%s *)xcb_tmp;' % (field.c_field_name, field.c_field_type)
temp_vars.append(' %s *%s;' % (field.type.c_type, field.c_field_name))
# special case: switch
if 'unpack' == context:
if context.startswith('unpack'):
value = ' %s%s = (%s *)xcb_tmp;' % (prefix_str, field.c_field_name, field.c_field_type)
elif 'serialize' == context:
@ -1175,6 +1204,12 @@ def _c_serialize_helper_fields_variable_size(context, self, field,
return (value, length)
def _c_emit_bounds_check(code_lines, space, length):
code_lines.append('%s if ((const char *)xcb_end < (const char *)xcb_tmp)' % (space,))
code_lines.append('%s abort();' % (space,))
code_lines.append('%s if ((size_t)((const char *)xcb_end - (const char *)xcb_tmp) < %s)' % (space, length))
code_lines.append('%s return -1;' % (space,))
def _c_serialize_helper_fields(context, self,
code_lines, temp_vars,
space, prefix, is_case_or_bitcase):
@ -1188,7 +1223,7 @@ def _c_serialize_helper_fields(context, self,
if not field.wire:
continue
if not field.visible:
if not ((field.wire and not field.auto) or 'unserialize' == context):
if not ((field.wire and not field.auto) or context.startswith('unserialize')):
continue
# switch/bitcase: fixed size fields must be considered explicitly
@ -1239,24 +1274,39 @@ def _c_serialize_helper_fields(context, self,
if field.type.fixed_size():
if is_case_or_bitcase or self.c_var_followed_by_fixed_fields:
# keep track of (un)serialized object's size
code_lines.append('%s xcb_block_len += %s;' % (space, length))
assert 'xcb_block_len' not in length
if context in ('unserialize', 'unpack', 'sizeof'):
code_lines.append('%s xcb_tmp += %s;' % (space, length))
elif context in ('unserialize_checked', 'sizeof_checked', 'unpack_checked'):
_c_emit_bounds_check(code_lines, space, length)
code_lines.append('%s xcb_tmp += %s;' % (space, length))
code_lines.append('%s xcb_block_len += %s;' % (space, length))
else:
# variable size objects or bitcase:
# value & length might have been inserted earlier for special cases
if '' != length:
# special case: intermixed fixed and variable size fields
if (not field.type.fixed_size() and
self.c_var_followed_by_fixed_fields and 'unserialize' == context):
self.c_var_followed_by_fixed_fields and context.startswith('unserialize')):
temp_vars.append(' int %s_len;' % field.c_field_name)
code_lines.append('%s %s_len = %s;' % (space, field.c_field_name, length))
code_lines.append('%s if (__builtin_add_overflow(xcb_block_len, %s_len, &xcb_block_len))' % (space, field.c_field_name))
if context.endswith('checked'):
code_lines.append('%s return -1;' % (space,))
_c_emit_bounds_check(code_lines, space, field.c_field_name)
else:
code_lines.append('%s abort();' % (space,))
code_lines.append('%s xcb_block_len += %s_len;' % (space, field.c_field_name))
code_lines.append('%s xcb_tmp += %s_len;' % (space, field.c_field_name))
else:
code_lines.append('%s xcb_block_len += %s;' % (space, length))
code_lines.append('%s if (__builtin_add_overflow(xcb_block_len, %s, &xcb_block_len))' % (space, length))
if context.endswith('checked'):
code_lines.append('%s return -1;' % (space,))
_c_emit_bounds_check(code_lines, space, 'xcb_block_len')
else:
code_lines.append('%s abort();' % (space,))
# increase pointer into the byte stream accordingly
if context in ('unserialize', 'sizeof', 'unpack'):
if context in ('unserialize', 'unserialize_checked', 'sizeof', 'unpack', 'unpack_checked', 'sizeof_checked'):
code_lines.append('%s xcb_tmp += xcb_block_len;' % space)
if 'serialize' == context:
@ -1266,7 +1316,7 @@ def _c_serialize_helper_fields(context, self,
count += 1
code_lines.append(
'%s xcb_align_to = ALIGNOF(%s);'
'%s xcb_align_to = alignof(%s);'
% (space,
'char'
if field.c_field_type == 'void' or field.type.is_switch
@ -1305,8 +1355,13 @@ def _c_serialize_helper(context, complex_type,
# all other data types can be evaluated one field a time
else:
# unserialize & fixed size fields: simply cast the buffer to the respective xcb_out type
if context in ('unserialize', 'unpack', 'sizeof') and not self.c_var_followed_by_fixed_fields:
code_lines.append('%s xcb_block_len += sizeof(%s);' % (space, self.c_type))
if context != 'serialize' and not self.c_var_followed_by_fixed_fields:
code_lines.append('%s if (__builtin_add_overflow(xcb_block_len, sizeof(%s), &xcb_block_len))' % (space, self.c_type))
if context.endswith('checked'):
code_lines.append('%s return -1;' % (space,))
_c_emit_bounds_check(code_lines, space, 'xcb_block_len')
else:
code_lines.append('%s abort();' % (space,))
code_lines.append('%s xcb_tmp += xcb_block_len;' % space)
code_lines.append('%s xcb_buffer_len += xcb_block_len;' % space)
code_lines.append('%s xcb_block_len = 0;' % space)
@ -1321,7 +1376,7 @@ def _c_serialize_helper(context, complex_type,
def _c_serialize(context, self):
"""
depending on the context variable, generate _serialize(), _unserialize(), _unpack(), or _sizeof()
depending on the context variable, generate _serialize(), _unserialize(), _unpack(), _sizeof(), or _sizeof_checked()
for the ComplexType variable self
"""
_h_setlevel(1)
@ -1331,13 +1386,16 @@ def _c_serialize(context, self):
# _serialize() returns the buffer size
_hc('int')
if self.is_switch and 'unserialize' == context:
context = 'unpack'
if self.is_switch and context.startswith('unserialize'):
context = context.replace('serialize', 'pack')
cases = { 'serialize' : self.c_serialize_name,
'unserialize' : self.c_unserialize_name,
'unpack' : self.c_unpack_name,
'sizeof' : self.c_sizeof_name }
cases = { 'serialize' : self.c_serialize_name,
'unserialize' : self.c_unserialize_name,
'unserialize_checked' : self.c_unserialize_name + '_checked',
'unpack' : self.c_unpack_name,
'unpack_checked' : self.c_unpack_name + '_checked',
'sizeof' : self.c_sizeof_name,
'sizeof_checked' : self.c_sizeof_name + '_checked' }
func_name = cases[context]
param_fields, wire_fields, params = get_serialize_params(context, self)
@ -1391,7 +1449,7 @@ def _c_serialize(context, self):
prefix = [('_aux', '->', self)]
aux_ptr = 'xcb_out'
elif context in ('unserialize', 'unpack'):
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked'):
_c(' char *xcb_tmp = (char *)_buffer;')
if not self.is_switch:
if not self.c_var_followed_by_fixed_fields:
@ -1415,22 +1473,27 @@ def _c_serialize(context, self):
_c(' unsigned int xcb_padding_offset = %d;',
self.get_align_offset() )
elif 'sizeof' == context:
elif context in ('sizeof', 'sizeof_checked'):
param_names = [p[2] for p in params]
if self.length_expr is not None:
_c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type)
prefix = [('_aux', '->', self)]
field_mapping = _c_get_field_mapping_for_expr(self, self.length_expr, prefix)
_c(' return %s;', _c_accessor_get_expr(self.length_expr, field_mapping))
v = _c_accessor_get_expr(self.length_expr, field_mapping, checked=context.endswith('checked'))
_c('')
for t in temp_vars:
_c(t)
_c('')
for l in code_lines:
_c(l)
_c(' return %s;', v)
_c('}')
_c_pre.redirect_end()
return
if self.is_switch:
# switch: call _unpack()
_c(' %s _aux;', self.c_type)
_c(' return %s(%s, &_aux);', self.c_unpack_name, ", ".join(param_names))
_c(' return %s%s(%s, &_aux);', self.c_unpack_name, context[6:], ", ".join(param_names))
_c('}')
_c_pre.redirect_end()
return
@ -1445,6 +1508,8 @@ def _c_serialize(context, self):
prefix = [('_aux', '->', self)]
if self.is_switch:
_c(' unsigned int xcb_padding_offset = 0;')
else:
assert False, 'bad context %r' % context
count = _c_serialize_helper(context, self, code_lines, temp_vars, prefix=prefix)
# update variable size fields (only important for context=='serialize'
@ -1457,7 +1522,7 @@ def _c_serialize(context, self):
temp_vars.append(' unsigned int xcb_block_len = 0;')
temp_vars.append(' unsigned int i;')
temp_vars.append(' char *xcb_tmp;')
elif 'sizeof' == context:
elif context.startswith('sizeof'):
# neither switch nor intermixed fixed and variable size fields:
# evaluate parameters directly
if not (self.is_switch or self.c_var_followed_by_fixed_fields):
@ -1486,9 +1551,9 @@ def _c_serialize(context, self):
# variable sized fields have been collected, now
# allocate memory and copy everything into a continuous memory area
# note: this is not necessary in case of unpack
if context in ('serialize', 'unserialize'):
if context in ('serialize', 'unserialize', 'unserialize_checked'):
# unserialize: check for sizeof-only invocation
if 'unserialize' == context:
if context.startswith('unserialize'):
_c('')
_c(' if (NULL == _aux)')
_c(' return xcb_buffer_len;')
@ -1497,6 +1562,8 @@ def _c_serialize(context, self):
_c(' if (NULL == %s) {', aux_ptr)
_c(' /* allocate memory */')
_c(' %s = malloc(xcb_buffer_len);', aux_ptr)
_c(' if (%s == NULL)', aux_ptr)
_c(' return -1;')
if 'serialize' == context:
_c(' *_buffer = xcb_out;')
_c(' }')
@ -1525,8 +1592,8 @@ def _c_serialize(context, self):
_c(' }')
# unserialize: assign variable size fields individually
if 'unserialize' == context:
_c(' xcb_tmp = ((char *)*_aux)+xcb_buffer_len;')
if context.startswith('unserialize'):
_c(' xcb_tmp = ((const char *)*_aux)+xcb_buffer_len;')
param_fields.reverse()
for field in param_fields:
if not field.type.fixed_size():
@ -1575,7 +1642,7 @@ def _c_iterator(self, name):
param[0],
' ' * (len(self.c_type) + 1 - len(param[0])),
param[2])
_h('} %s;', self.c_iterator_type)
_h('} XCB_MAY_ALIAS %s;', self.c_iterator_type)
_h_setlevel(1)
_c_setlevel(1)
@ -1673,7 +1740,7 @@ def _c_accessor_get_length(expr, field_mapping=None):
else:
return str(expr.nmemb)
def _c_accessor_get_expr(expr, field_mapping):
def _c_accessor_get_expr(expr, field_mapping, *, checked):
'''
Figures out what C code is needed to get the length of a list field.
The field_mapping parameter can be used to change the absolute name of a length field.
@ -1683,11 +1750,11 @@ def _c_accessor_get_expr(expr, field_mapping):
'''
lenexp = _c_accessor_get_length(expr, field_mapping)
if expr.op == '~':
return '(' + '~' + _c_accessor_get_expr(expr.rhs, field_mapping) + ')'
elif expr.op == 'popcount':
return 'xcb_popcount(' + _c_accessor_get_expr(expr.rhs, field_mapping) + ')'
elif expr.op == 'enumref':
if expr.op == '~': # SAFE
return '(' + '~' + _c_accessor_get_expr(expr.rhs, field_mapping, checked=checked) + ')'
elif expr.op == 'popcount': # SAFE
return 'xcb_popcount(' + _c_accessor_get_expr(expr.rhs, field_mapping, checked=checked) + ')'
elif expr.op == 'enumref': # SAFE
enum_name = expr.lenfield_type.name
constant_name = expr.lenfield_name
c_name = _n(enum_name + (constant_name,)).upper()
@ -1697,7 +1764,7 @@ def _c_accessor_get_expr(expr, field_mapping):
field = expr.lenfield
list_name = field_mapping[field.c_field_name][0]
c_length_func = "%s(%s)" % (field.c_length_name, list_name)
c_length_func = _c_accessor_get_expr(field.type.expr, field_mapping)
c_length_func = _c_accessor_get_expr(field.type.expr, field_mapping, checked=checked)
# create explicit code for computing the sum.
# This works for all C-types which can be added to int64_t with +=
_c_pre.start()
@ -1742,11 +1809,16 @@ def _c_accessor_get_expr(expr, field_mapping):
# cause pre-code of the subexpression be added right here
_c_pre.end()
# compute the subexpression
rhs_expr_str = _c_accessor_get_expr(expr.rhs, scoped_field_mapping)
rhs_expr_str = _c_accessor_get_expr(expr.rhs, scoped_field_mapping, checked=checked)
# resume with our code
_c_pre.start()
# output the summation expression
_c_pre.code("%s += %s;", sumvar, rhs_expr_str)
if checked:
_c_pre.code("if (__builtin_add_overflow(%s, %s, &%s))", sumvar, rhs_expr_str, sumvar)
_c_pre.code(" /* SUMOF */")
_c_pre.code(" return -1;")
else:
_c_pre.code("%s += %s;", sumvar, rhs_expr_str)
_c_pre.code("%s++;", listvar)
_c_pre.pop_indent()
@ -1757,9 +1829,39 @@ def _c_accessor_get_expr(expr, field_mapping):
elif expr.op == 'listelement-ref':
return '(*xcb_listelement)'
elif expr.op != None and expr.op != 'calculate_len':
return ('(' + _c_accessor_get_expr(expr.lhs, field_mapping) +
' ' + expr.op + ' ' +
_c_accessor_get_expr(expr.rhs, field_mapping) + ')')
_c_pre.start()
sumvar = _c_pre.get_tempvarname()
match expr.op:
case '+':
op = 'add'
case '-':
op = 'sub'
case '*':
op = 'mul'
case '/':
_c_pre.tempvar("int64_t %s;", sumvar)
_c_pre.code("%s = (%s);",
sumvar,
_c_accessor_get_expr(expr.rhs, field_mapping, checked=checked))
_c_pre.code("if (%s < 1)", sumvar)
_c_pre.code(" return -1;" if checked else " abort();")
_c_pre.end()
return ('((' + _c_accessor_get_expr(expr.lhs, field_mapping, checked=checked) + ') / ' + sumvar + ')')
case '&':
_c_pre.end()
return ('(' + _c_accessor_get_expr(expr.lhs, field_mapping, checked=checked) +
' & ' + _c_accessor_get_expr(expr.rhs, field_mapping, checked=checked) + ')')
case _:
assert False
_c_pre.tempvar("int64_t %s; /* sum */", sumvar)
_c_pre.code("if (__builtin_%s_overflow((%s), (%s), &%s))",
op,
_c_accessor_get_expr(expr.lhs, field_mapping, checked=checked),
_c_accessor_get_expr(expr.rhs, field_mapping, checked=checked),
sumvar)
_c_pre.code(" return -1;" if checked else " abort();")
_c_pre.end()
return ('(' + sumvar + ')')
elif expr.bitfield:
return 'xcb_popcount(' + lenexp + ')'
else:
@ -1971,7 +2073,7 @@ def _c_accessors_list(self, field):
(field.c_field_name)
);
else:
return _c_accessor_get_expr(field.type.expr, fields)
return _c_accessor_get_expr(field.type.expr, fields, checked=False)
_c(' return %s;', get_length())
_c('}')
@ -2152,7 +2254,7 @@ def _c_complex(self, force_packed = False):
if b.type.has_name:
_h(' } %s;', b.c_field_name)
_h('} %s%s;', 'XCB_PACKED ' if force_packed else '', self.c_type)
_h('} %sXCB_MAY_ALIAS %s;', 'XCB_PACKED ' if force_packed else '', self.c_type)
def c_struct(self, name):
'''
@ -2367,7 +2469,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
_c(' void *xcb_aux%d = 0;' % (idx))
if list_with_var_size_elems:
_c(' unsigned int xcb_tmp_len;')
_c(' char *xcb_tmp;')
_c(' const char *xcb_tmp;')
num_fds_fixed = 0
num_fds_expr = []
@ -2376,7 +2478,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
if not field.type.is_list:
num_fds_fixed += 1
else:
num_fds_expr.append(_c_accessor_get_expr(field.type.expr, None))
num_fds_expr.append(_c_accessor_get_expr(field.type.expr, None, checked=True))
if list_with_var_size_elems or len(num_fds_expr) > 0:
_c(' unsigned int i;')
@ -2396,7 +2498,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
for field in wire_fields:
if field.type.fixed_size():
if field.type.is_expr:
_c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr, None))
_c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr, None, checked=True))
elif field.type.is_pad:
if field.type.nmemb == 1:
_c(' xcb_out.%s = 0;', field.c_field_name)
@ -2437,12 +2539,12 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
if field.type.expr.op == 'calculate_len':
lenfield = field.type.expr.lenfield_name
else:
lenfield = _c_accessor_get_expr(field.type.expr, None)
lenfield = _c_accessor_get_expr(field.type.expr, None, checked=False)
_c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, lenfield,
field.type.member.c_wiretype)
else:
list_length = _c_accessor_get_expr(field.type.expr, None)
list_length = _c_accessor_get_expr(field.type.expr, None, checked=False)
length = ''
_c(" xcb_parts[%d].iov_len = 0;" % count)
@ -2500,7 +2602,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
if not field.type.is_list:
_c(' fds[fd_index++] = %s;', field.c_field_name)
else:
_c(' for (i = 0; i < %s; i++)', _c_accessor_get_expr(field.type.expr, None))
_c(' for (i = 0; i < %s; i++)', _c_accessor_get_expr(field.type.expr, None, checked=False))
_c(' fds[fd_index++] = %s[i];', field.c_field_name)
if not num_fds:
@ -2566,34 +2668,39 @@ def _c_reply(self, name):
_h('%sxcb_generic_error_t%s **e);', spacing3, spacing2)
_c('%sxcb_generic_error_t%s **e)', spacing3, spacing2)
_c('{')
if len(unserialize_fields)>0:
# certain variable size fields need to be unserialized explicitly
_c(' %s *reply = (%s *) xcb_wait_for_reply(c, cookie.sequence, e);',
if not self.reply.fixed_size:
_c(' %s *reply = (%s *) xcb_wait_for_reply_safe(c, cookie.sequence, e, sizeof (*reply));',
self.c_reply_type, self.c_reply_type)
_c(' int i;')
for field in unserialize_fields:
if field.type.is_list:
_c(' %s %s_iter = %s(reply);', field.c_iterator_type, field.c_field_name, field.c_iterator_name)
_c(' int %s_len = %s(reply);', field.c_field_name, field.c_length_name)
_c(' %s *%s_data;', field.c_field_type, field.c_field_name)
else:
raise Exception('not implemented: call _unserialize() in reply for non-list type %s', field.c_field_type)
# call _unserialize(), using the reply as source and target buffer
_c(' /* special cases: transform parts of the reply to match XCB data structures */')
for field in unserialize_fields:
if field.type.is_list:
_c(' for(i=0; i<%s_len; i++) {', field.c_field_name)
_c(' %s_data = %s_iter.data;', field.c_field_name, field.c_field_name)
_c(' %s((const void *)%s_data, &%s_data);', field.type.c_unserialize_name,
field.c_field_name, field.c_field_name)
_c(' %s(&%s_iter);', field.type.c_next_name, field.c_field_name)
_c(' }')
# return the transformed reply
_c(' if (!reply)')
_c(' return NULL;')
_c(' if (%s_checked(reply) < 0) {', self.c_sizeof_name)
_c(' free(reply);')
_c(' return NULL;')
_c(' }')
if len(unserialize_fields)>0:
# certain variable size fields need to be unserialized explicitly
_c(' int i;')
for field in unserialize_fields:
if field.type.is_list:
_c(' %s %s_iter = %s(reply);', field.c_iterator_type, field.c_field_name, field.c_iterator_name)
_c(' int %s_len = %s(reply);', field.c_field_name, field.c_length_name)
_c(' %s *%s_data;', field.c_field_type, field.c_field_name)
else:
raise Exception('not implemented: call _unserialize() in reply for non-list type %s', field.c_field_type)
# call _unserialize(), using the reply as source and target buffer
_c(' /* special cases: transform parts of the reply to match XCB data structures */')
for field in unserialize_fields:
if field.type.is_list:
_c(' for(i=0; i<%s_len; i++) {', field.c_field_name)
_c(' %s_data = %s_iter.data;', field.c_field_name, field.c_field_name)
_c(' %s((const void *)%s_data, &%s_data);', field.type.c_unserialize_name,
field.c_field_name, field.c_field_name)
_c(' %s(&%s_iter);', field.type.c_next_name, field.c_field_name)
_c(' }')
# return the transformed reply
_c(' return reply;')
else:
_c(' return (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', self.c_reply_type)
_c(' return (%s *) xcb_wait_for_reply_safe(c, cookie.sequence, e, sizeof (%s));', self.c_reply_type, self.c_reply_type)
_c('}')
@ -2649,7 +2756,7 @@ def _c_cookie(self, name):
_h(' **/')
_h('typedef struct %s {', self.c_cookie_type)
_h(' unsigned int sequence;')
_h('} %s;', self.c_cookie_type)
_h('} XCB_MAY_ALIAS %s;', self.c_cookie_type)
def _man_request(self, name, void, aux):
param_fields = [f for f in self.fields if f.visible]
@ -2705,7 +2812,7 @@ def _man_request(self, name, void, aux):
f.write('.SS Reply datastructure\n')
f.write('.nf\n')
f.write('.sp\n')
f.write('typedef %s %s {\n' % (self.reply.c_container, self.reply.c_type))
f.write('typedef XCB_MAY_ALIAS %s %s {\n' % (self.reply.c_container, self.reply.c_type))
struct_fields = []
maxtypelen = 0
@ -3282,7 +3389,7 @@ def c_event(self, name):
else:
# Typedef
_h('')
_h('typedef %s %s;', _t(self.name + ('event',)), _t(name + ('event',)))
_h('typedef XCB_MAY_ALIAS %s %s;', _t(self.name + ('event',)), _t(name + ('event',)))
# Create sizeof-function for eventcopies for compatibility reasons
if self.c_need_sizeof:
@ -3317,7 +3424,7 @@ def c_error(self, name):
else:
# Typedef
_h('')
_h('typedef %s %s;', _t(self.name + ('error',)), _t(name + ('error',)))
_h('typedef XCB_MAY_ALIAS %s %s;', _t(self.name + ('error',)), _t(name + ('error',)))
# Main routine starts here

View File

@ -66,8 +66,14 @@ extern "C" {
/* Supported in gcc 2.7 and later */
#if defined(__GNUC__) || __has_attribute(__packed__)
#define XCB_PACKED __attribute__((__packed__))
#ifndef XCB_MAY_ALIAS
#define XCB_MAY_ALIAS __attribute__((may_alias))
#endif
#else
#define XCB_PACKED
#ifndef XCB_MAY_ALIAS
#define XCB_MAY_ALIAS
#endif
#endif
/* Supported in gcc 2.96 and later */

View File

@ -30,12 +30,14 @@
#endif
#include <assert.h>
#include <stdalign.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include "xcb.h"
#include "xcbint.h"
@ -168,8 +170,23 @@ static int write_setup(xcb_connection_t *c, xcb_auth_info_t *auth_info)
return ret;
}
/* A bunch of static assertions */
#define ASSERT_SIZE_ALIGN(t, size, align) \
static_assert(sizeof(t) == size, "unexpected size of" #t); \
static_assert(alignof(t) == align, "unexpected alignment of" #t);
ASSERT_SIZE_ALIGN(xcb_screen_t, 40, 4);
ASSERT_SIZE_ALIGN(xcb_setup_t, 40, 4);
ASSERT_SIZE_ALIGN(xcb_setup_generic_t, 8, 2);
ASSERT_SIZE_ALIGN(xcb_setup_authenticate_t, 8, 2);
ASSERT_SIZE_ALIGN(xcb_setup_failed_t, 8, 2);
ASSERT_SIZE_ALIGN(xcb_visualtype_t, 24, 4);
ASSERT_SIZE_ALIGN(xcb_depth_t, 8, 2);
ASSERT_SIZE_ALIGN(xcb_format_t, 8, 1);
#undef ASSERT_SIZE_ALIGN
static int read_setup(xcb_connection_t *c)
{
uint32_t extra_bytes, total_bytes;
const char newline = '\n';
/* Read the server response */
@ -180,14 +197,16 @@ static int read_setup(xcb_connection_t *c)
if(_xcb_in_read_block(c, c->setup, sizeof(xcb_setup_generic_t)) != sizeof(xcb_setup_generic_t))
return 0;
extra_bytes = c->setup->length * UINT32_C(4);
total_bytes = extra_bytes + sizeof(xcb_setup_generic_t);
{
void *tmp = realloc(c->setup, c->setup->length * 4 + sizeof(xcb_setup_generic_t));
void *tmp = realloc(c->setup, total_bytes);
if(!tmp)
return 0;
c->setup = tmp;
}
if(_xcb_in_read_block(c, (char *) c->setup + sizeof(xcb_setup_generic_t), c->setup->length * 4) <= 0)
if(_xcb_in_read_block(c, (char *) c->setup + sizeof(xcb_setup_generic_t), extra_bytes) <= 0)
return 0;
/* 0 = failed, 2 = authenticate, 1 = success */
@ -196,21 +215,162 @@ static int read_setup(xcb_connection_t *c)
case 0: /* failed */
{
xcb_setup_failed_t *setup = (xcb_setup_failed_t *) c->setup;
write(STDERR_FILENO, xcb_setup_failed_reason(setup), xcb_setup_failed_reason_length(setup));
char *msg = (char *)c->setup + sizeof(*setup);
if (setup->reason_len > extra_bytes)
setup->reason_len = (uint8_t)extra_bytes;
for (size_t i = 0; i < setup->reason_len; ++i) {
if (msg[i] < 0x20 || msg[i] > 0x7E)
msg[i] = 0x20;
}
write(STDERR_FILENO, msg, setup->reason_len);
write(STDERR_FILENO, &newline, 1);
return 0;
}
case 2: /* authenticate */
{
xcb_setup_authenticate_t *setup = (xcb_setup_authenticate_t *) c->setup;
write(STDERR_FILENO, xcb_setup_authenticate_reason(setup), xcb_setup_authenticate_reason_length(setup));
char *reason = (char *)c->setup + sizeof(xcb_setup_authenticate_t);
for (size_t i = 0; i < extra_bytes; ++i) {
if (reason[i] < 0x20 || reason[i] > 0x7E)
reason[i] = 0x20;
}
write(STDERR_FILENO, reason, extra_bytes);
write(STDERR_FILENO, &newline, 1);
return 0;
}
}
case 1: /* success */
{
const uint8_t *cursor = (const uint8_t *)c->setup;
const uint8_t *end = cursor + total_bytes;
xcb_setup_t *const setup = (xcb_setup_t *)cursor;
const xcb_format_t *formats;
uint32_t pixmap_offset, pixmap_formats_len;
size_t i, j;
return 1;
if (total_bytes < sizeof(xcb_setup_t) + sizeof(xcb_screen_t) +
sizeof(xcb_format_t))
return 0; /* not enough bytes sent, no screens, or no formats */
if (setup->roots_len < 1 || setup->pixmap_formats_len < 1)
return 0; /* no screens or no formats */
/* Offset of the pixmaps. Cannot exceed 65576. */
static_assert(sizeof(setup->vendor_len) == 2,
"wrong size of setup->vendor_len");
pixmap_offset = (sizeof(*setup) + (uint32_t)setup->vendor_len + 3) & ~3;
/* Length of the pixmap formats. Cannot exceed 2040. */
static_assert(sizeof(setup->pixmap_formats_len) == 1,
"wrong size of setup->pixmap_formats_len");
pixmap_formats_len = sizeof(xcb_format_t) * (uint32_t)setup->pixmap_formats_len;
/* Offset of the screens. Max RHS = 65576 + 2040 = 67616
* so no risk of overflow */
uint32_t const screen_offset = pixmap_offset + pixmap_formats_len;
/* 4 zero bytes, for memcmp() */
uint32_t const zero = 0;
/* First screen must fit in the data sent. */
if (total_bytes < screen_offset + sizeof(xcb_screen_t))
return 0;
/*
* xcb_setup_pixmap_formats() would be safe, but just using pointer
* arithmetic is simpler.
*/
formats = (const xcb_format_t *)(cursor + pixmap_offset);
/* Validate the pixmap formats. Bounds have been checked already. */
for (i = 0; i < setup->pixmap_formats_len; ++i) {
/* Depth must not be zero. */
if (formats[i].depth == 0)
return 0;
/* Bits per pixel must not be zero and must be a multiple of 8. */
if ((formats[i].bits_per_pixel < 8) ||
(formats[i].bits_per_pixel % 8) != 0)
return 0;
}
/*
* Alignment is guaranteed because screen_offset is a multiple of 4
* and c->setup was allocated by malloc(). cursor is kept 4-byte
* aligned at all times.
*/
cursor += screen_offset;
/* Validate each screen. */
for (i = 0; i < setup->roots_len; ++i) {
const struct xcb_screen_t *screen;
const struct xcb_depth_t *depth;
const struct xcb_visualtype_t *visuals;
/*
* Screen must fit in the buffer with room for a depth and a
* visual type.
*/
if ((end - cursor) <
(ptrdiff_t)(sizeof(*screen) + sizeof(*depth) + sizeof(*visuals)))
return 0;
screen = (const struct xcb_screen_t *)cursor;
cursor += sizeof(*screen);
/* Screens must have at least one depth */
if (screen->allowed_depths_len < 1)
return 0;
for (j = 0; j < screen->allowed_depths_len; ++j) {
/* Depth must fit in the buffer with room for a visual type. */
if ((end - cursor) < (ptrdiff_t)(sizeof(*depth) + sizeof(*visuals)))
return 0;
/*
* Alignment is guaranteed because xcb_screen_t has greater
* alignment (4) than xcb_depth_t (2). sizeof(xcb_depth_t)
* is a multiple of 4 so alignment will be maintained.
*/
depth = (const xcb_depth_t *)cursor;
visuals = (const xcb_visualtype_t *)(depth + 1);
/* Padding must be zero */
if ((depth->pad0 != 0) ||
(memcmp(depth->pad1, &zero, sizeof(zero)) != 0))
return 0;
/* Depths must have at least one visual type */
if (depth->visuals_len < 1)
return 0;
/* depth->visuals_len is uint16_t so overflow is impossible. */
static_assert(sizeof(depth->visuals_len) == 2,
"wrong size of setup->visuals_len");
/* Visuals must fit in the buffer. */
if ((size_t)(end - (const uint8_t *)visuals) <
(uint32_t)depth->visuals_len * sizeof(*visuals))
return 0;
cursor = (const uint8_t *)(visuals + depth->visuals_len);
while ((const uint8_t *)visuals < cursor) {
/* Padding must be zero. */
if (memcmp(visuals->pad0, &zero, 4))
return 0;
/* Bits per RGB value must not be zero. */
if (visuals->bits_per_rgb_value < 1)
return 0;
visuals++;
}
}
}
if (end != cursor)
return 0; /* trailing junk */
}
return 1;
default:
return 0;
}
}
/* precondition: there must be something for us to write. */

View File

@ -34,6 +34,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#if USE_POLL
#include <poll.h>
@ -251,7 +252,7 @@ static int read_packet(xcb_connection_t *c)
/* XXX a bit of a hack -- we "know" that all FD replys place
* the number of fds in the pad0 byte */
if (pend && pend->flags & XCB_REQUEST_REPLY_FDS)
if (pend && (pend->flags & XCB_REQUEST_REPLY_FDS))
nfd = genrep.pad0;
}
@ -261,7 +262,7 @@ static int read_packet(xcb_connection_t *c)
bufsize = length + eventlength + nfd * sizeof(int) +
(genrep.response_type == XCB_REPLY ? 0 : sizeof(uint32_t));
if (bufsize < INT32_MAX)
if (bufsize < INT32_MAX && length <= INT_MAX && eventlength <= INT_MAX)
buf = malloc((size_t) bufsize);
else
buf = NULL;
@ -415,6 +416,7 @@ static int poll_for_reply(xcb_connection_t *c, uint64_t request, void **reply, x
{
struct reply_list *head;
*reply = NULL;
/* If an error occurred when issuing the request, fail immediately. */
if(!request)
head = 0;
@ -446,7 +448,6 @@ static int poll_for_reply(xcb_connection_t *c, uint64_t request, void **reply, x
if(error)
*error = 0;
*reply = 0;
if(head)
{
@ -510,6 +511,12 @@ static void *wait_for_reply(xcb_connection_t *c, uint64_t request, xcb_generic_e
{
void *ret = 0;
if (e)
*e = NULL;
if (c->has_error)
return NULL;
/* If this request has not been written yet, write it. */
if(c->out.return_socket || _xcb_out_flush_to(c, request))
{
@ -538,16 +545,24 @@ static uint64_t widen(xcb_connection_t *c, unsigned int request)
return widened_request;
}
static void *wait_for_reply_safe(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e, size_t size)
{
xcb_generic_reply_t *ret = NULL;
ret = wait_for_reply(c, request, e);
if (ret && size > 32 && ret->length < ((size - 29) >> 2))
{
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_PARSE_ERR);
free(ret);
ret = NULL;
}
return ret;
}
/* Public interface */
void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e)
{
void *ret;
if(e)
*e = 0;
if(c->has_error)
return 0;
pthread_mutex_lock(&c->iolock);
ret = wait_for_reply(c, widen(c, request), e);
pthread_mutex_unlock(&c->iolock);
@ -557,17 +572,30 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
void *xcb_wait_for_reply64(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e)
{
void *ret;
if(e)
*e = 0;
if(c->has_error)
return 0;
pthread_mutex_lock(&c->iolock);
ret = wait_for_reply(c, request, e);
pthread_mutex_unlock(&c->iolock);
return ret;
}
void *xcb_wait_for_reply_safe(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e, size_t size)
{
xcb_generic_reply_t *ret = NULL;
pthread_mutex_lock(&c->iolock);
ret = wait_for_reply_safe(c, widen(c, request), e, size);
pthread_mutex_unlock(&c->iolock);
return ret;
}
void *xcb_wait_for_reply64_safe(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e, size_t size)
{
xcb_generic_reply_t *ret = NULL;
pthread_mutex_lock(&c->iolock);
ret = wait_for_reply_safe(c, request, e, size);
pthread_mutex_unlock(&c->iolock);
return ret;
}
int *xcb_get_reply_fds(xcb_connection_t *c, void *reply, size_t reply_size)
{
return (int *) (&((char *) reply)[reply_size]);
@ -599,6 +627,9 @@ static void discard_reply(xcb_connection_t *c, uint64_t request)
void *reply;
pending_reply **prev_pend;
if (c->has_error)
return;
/* Free any replies or errors that we've already read. Stop if
* xcb_wait_for_reply would block or we've run out of replies. */
while(poll_for_reply(c, request, &reply, 0) && reply)
@ -628,9 +659,6 @@ static void discard_reply(xcb_connection_t *c, uint64_t request)
void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence)
{
if(c->has_error)
return;
/* If an error occurred when issuing the request, fail immediately. */
if(!sequence)
return;
@ -642,9 +670,6 @@ void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence)
void xcb_discard_reply64(xcb_connection_t *c, uint64_t sequence)
{
if(c->has_error)
return;
/* If an error occurred when issuing the request, fail immediately. */
if(!sequence)
return;
@ -654,9 +679,10 @@ void xcb_discard_reply64(xcb_connection_t *c, uint64_t sequence)
pthread_mutex_unlock(&c->iolock);
}
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error)
static int poll_for_reply_inner(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error)
{
int ret;
assert(reply != 0);
if(c->has_error)
{
*reply = 0;
@ -664,11 +690,33 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
*error = 0;
return 1; /* would not block */
}
assert(reply != 0);
pthread_mutex_lock(&c->iolock);
ret = poll_for_reply(c, widen(c, request), reply, error);
ret = poll_for_reply(c, request, reply, error);
if(!ret && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
ret = poll_for_reply(c, widen(c, request), reply, error);
ret = poll_for_reply(c, request, reply, error);
return ret;
}
static int poll_for_reply_inner_safe(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error, size_t size)
{
int ret = poll_for_reply_inner(c, request, reply, error);
if (ret && *reply)
{
xcb_generic_reply_t *genrep = *reply;
if (size > 32 && genrep->length < ((size - 29) >> 2))
{
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_PARSE_ERR);
free(genrep);
*reply = NULL;
}
}
return ret;
}
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error)
{
int ret;
pthread_mutex_lock(&c->iolock);
ret = poll_for_reply_inner(c, widen(c, request), reply, error);
pthread_mutex_unlock(&c->iolock);
return ret;
}
@ -676,34 +724,43 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
int xcb_poll_for_reply64(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error)
{
int ret;
if(c->has_error)
{
*reply = 0;
if(error)
*error = 0;
return 1; /* would not block */
}
assert(reply != 0);
pthread_mutex_lock(&c->iolock);
ret = poll_for_reply(c, request, reply, error);
if(!ret && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
ret = poll_for_reply(c, request, reply, error);
ret = poll_for_reply_inner(c, request, reply, error);
pthread_mutex_unlock(&c->iolock);
return ret;
}
int xcb_poll_for_reply_safe(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error, size_t size)
{
int ret;
pthread_mutex_lock(&c->iolock);
ret = poll_for_reply_inner_safe(c, widen(c, request), reply, error, size);
pthread_mutex_unlock(&c->iolock);
return ret;
}
int xcb_poll_for_reply64_safe(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error, size_t size)
{
int ret;
pthread_mutex_lock(&c->iolock);
ret = poll_for_reply_inner_safe(c, request, reply, error, size);
pthread_mutex_unlock(&c->iolock);
return ret;
}
xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c)
{
xcb_generic_event_t *ret;
if(c->has_error)
return 0;
xcb_generic_event_t *ret = NULL;
pthread_mutex_lock(&c->iolock);
/* get_event returns 0 on empty list. */
while(!(ret = get_event(c)))
if(!_xcb_conn_wait(c, &c->in.event_cond, 0, 0))
break;
if(!c->has_error)
{
/* get_event returns 0 on empty list. */
while(!(ret = get_event(c)))
if(!_xcb_conn_wait(c, &c->in.event_cond, 0, 0))
break;
_xcb_in_wake_up_next_reader(c);
_xcb_in_wake_up_next_reader(c);
}
pthread_mutex_unlock(&c->iolock);
return ret;
}
@ -711,15 +768,15 @@ xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c)
static xcb_generic_event_t *poll_for_next_event(xcb_connection_t *c, int queued)
{
xcb_generic_event_t *ret = 0;
pthread_mutex_lock(&c->iolock);
if(!c->has_error)
{
pthread_mutex_lock(&c->iolock);
/* FIXME: follow X meets Z architecture changes. */
ret = get_event(c);
if(!ret && !queued && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
ret = get_event(c);
pthread_mutex_unlock(&c->iolock);
}
pthread_mutex_unlock(&c->iolock);
return ret;
}
@ -738,9 +795,11 @@ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t co
uint64_t request;
xcb_generic_error_t *ret = 0;
void *reply;
if(c->has_error)
return 0;
pthread_mutex_lock(&c->iolock);
if(c->has_error) {
pthread_mutex_unlock(&c->iolock);
return 0;
}
request = widen(c, cookie.sequence);
if (XCB_SEQUENCE_COMPARE(request, >, c->in.request_completed))
{
@ -779,9 +838,11 @@ xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c,
{
xcb_generic_event_t *event;
if(c->has_error)
return 0;
pthread_mutex_lock(&c->iolock);
if(c->has_error) {
pthread_mutex_unlock(&c->iolock);
return 0;
}
event = get_special_event(c, se);
if(!event && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
event = get_special_event(c, se);
@ -795,9 +856,11 @@ xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
special_list special;
xcb_generic_event_t *event;
if(c->has_error)
return 0;
pthread_mutex_lock(&c->iolock);
if(c->has_error) {
pthread_mutex_unlock(&c->iolock);
return 0;
}
insert_special(&c->in.special_waiters, &special, se);
@ -822,12 +885,14 @@ xcb_register_for_special_xge(xcb_connection_t *c,
xcb_special_event_t *se;
const xcb_query_extension_reply_t *ext_reply;
if(c->has_error)
return NULL;
ext_reply = xcb_get_extension_data(c, ext);
if (!ext_reply)
return NULL;
pthread_mutex_lock(&c->iolock);
if (c->has_error) {
pthread_mutex_unlock(&c->iolock);
return NULL;
}
for (se = c->in.special_events; se; se = se->next) {
if (se->extension == ext_reply->major_opcode &&
se->eid == eid) {
@ -866,10 +931,11 @@ xcb_unregister_for_special_event(xcb_connection_t *c,
if (!se)
return;
if (c->has_error)
return;
pthread_mutex_lock(&c->iolock);
if (c->has_error) {
pthread_mutex_unlock(&c->iolock);
return NULL;
}
for (prev = &c->in.special_events; (s = *prev) != NULL; prev = &(s->next)) {
if (s == se) {

View File

@ -207,7 +207,7 @@ void xcb_send_fd(xcb_connection_t *c, int fd);
* All replies that are generated while the socket is owned externally have
* @p flags applied to them. For example, use XCB_REQUEST_CHECK if you don't
* want errors to go to xcb's normal error handling, but instead having to be
* picked up via xcb_wait_for_reply(), xcb_poll_for_reply() or
* picked up via xcb_wait_for_reply_safe(), xcb_poll_for_reply_safe() or
* xcb_request_check().
*/
int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent);
@ -236,11 +236,27 @@ int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t re
/* xcb_in.c */
/**
* @brief Wait for the reply of a given request, with bounds checking.
* @param c The connection to the X server.
* @param request Sequence number of the request as returned by xcb_send_request().
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
* @param min_size The minimum size of the reply, in bytes.
*
* Returns the reply to the given request or returns null in the event of
* errors. Blocks until the reply or error for the request arrives, or an I/O
* error occurs. The returned pointer is guaranteed to point to at least min_size
* bytes of data.
*/
void *xcb_wait_for_reply_safe(xcb_connection_t *c, unsigned int request,
xcb_generic_error_t **e, size_t min_size);
/**
* @brief Wait for the reply of a given request.
* @param c The connection to the X server.
* @param request Sequence number of the request as returned by xcb_send_request().
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
* @deprecated Does not perform bounds checks; use xcb_wait_for_reply_safe() instead.
*
* Returns the reply to the given request or returns null in the event of
* errors. Blocks until the reply or error for the request arrives, or an I/O
@ -253,6 +269,28 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
* @param c The connection to the X server.
* @param request 64-bit sequence number of the request as returned by xcb_send_request64().
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
* @param size The number of bytes expected in the reply. If the reply is too short,
* the connection will be shut down. If the return value is not null, it is guaranteed to
* point to size bytes of memory.
*
* Returns the reply to the given request or returns null in the event of
* errors. Blocks until the reply or error for the request arrives, or an I/O
* error occurs.
*
* Unlike its xcb_wait_for_reply_safe() counterpart, the given sequence number is not
* automatically "widened" to 64-bit.
*/
void *xcb_wait_for_reply64_safe(xcb_connection_t *c, uint64_t request,
xcb_generic_error_t **e, size_t size);
/**
* @brief Wait for the reply of a given request, with 64-bit sequence number
* @param c The connection to the X server.
* @param request 64-bit sequence number of the request as returned by xcb_send_request64().
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
* @deprecated This function does not perform bounds checks. If reply is not NULL on
* return, it is only guaranteed to point to 32 bytes of memory. Use
* xcb_wait_for_reply64_safe() instead.
*
* Returns the reply to the given request or returns null in the event of
* errors. Blocks until the reply or error for the request arrives, or an I/O
@ -269,10 +307,47 @@ void *xcb_wait_for_reply64(xcb_connection_t *c, uint64_t request, xcb_generic_er
* @param request Sequence number of the request as returned by xcb_send_request().
* @param reply Location to store the reply in, must not be NULL.
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
* @param size The number of bytes expected in the reply. If the reply is too short,
* the connection will be shut down. If reply points to a non-NULL pointer on return,
* that pointer is guaranteed to point to size bytes of memory, or 32 bytes if size is
* less than 32.
* @return 1 when the reply to the request was returned, else 0.
*
* Checks if the reply to the given request already received. Does not block.
*/
int xcb_poll_for_reply_safe(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error, size_t size);
/**
* @brief Poll for the reply of a given request, with 64-bit sequence number.
* @param c The connection to the X server.
* @param request 64-bit sequence number of the request as returned by xcb_send_request().
* @param reply Location to store the reply in, must not be NULL.
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
* @param size The number of bytes expected in the reply. If the reply is too short,
* the connection will be shut down. If reply points to a non-NULL pointer on return,
* that pointer is guaranteed to point to size bytes of memory, or 32 bytes if size is
* less than 32.
* @return 1 when the reply to the request was returned, else 0.
*
* Checks if the reply to the given request already received. Does not block.
*
* Unlike its xcb_poll_for_reply_safe() counterpart, the given sequence number is not
* automatically "widened" to 64-bit.
*/
int xcb_poll_for_reply64_safe(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error, size_t size);
/**
* @brief Poll for the reply of a given request.
* @param c The connection to the X server.
* @param request Sequence number of the request as returned by xcb_send_request().
* @param reply Location to store the reply in, must not be NULL.
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
* @return 1 when the reply to the request was returned, else 0.
* @deprecated This function does not perform bounds checks. If reply is not NULL on
* return, it is only guaranteed to point to 32 bytes of memory.
*
* Checks if the reply to the given request already received. Does not block.
*/
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error);
/**
@ -282,6 +357,8 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
* @param reply Location to store the reply in, must not be NULL.
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
* @return 1 when the reply to the request was returned, else 0.
* @deprecated This function does not perform bounds checks. If reply is not NULL on
* return, it is only guaranteed to point to 32 bytes of memory.
*
* Checks if the reply to the given request already received. Does not block.
*

View File

@ -8,4 +8,4 @@ Description: XCB Composite Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-xfixes
Libs: -L${libdir} -lxcb-composite
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Damage Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-xfixes
Libs: -L${libdir} -lxcb-damage
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Double Buffer Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-dbe
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB DPMS Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-dpms
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB DRI2 Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-dri2
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB DRI3 Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-dri3
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB GenericEvent Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-ge
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB GLX Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-glx
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Present Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-randr xcb-xfixes xcb-sync xcb-dri3
Libs: -L${libdir} -lxcb-present
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB RandR Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-render
Libs: -L${libdir} -lxcb-randr
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Record Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-record
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Render Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-render
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB X-Resource Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-res
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Screensaver Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-screensaver
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Shape Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-shape
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Shm Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-shm
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Sync Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-sync
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Xevie Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xevie
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB XFree86-DRI Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xf86dri
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB XFixes Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-render xcb-shape
Libs: -L${libdir} -lxcb-xfixes
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Xinerama Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xinerama
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB XInput Extension (EXPERIMENTAL)
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-xfixes
Libs: -L${libdir} -lxcb-xinput
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Keyboard Extension (EXPERIMENTAL)
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xkb
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Xprint Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xprint
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB SELinux Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xselinux
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB XTEST Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb
Libs: -L${libdir} -lxcb-xtest
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB Xv Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-shm
Libs: -L${libdir} -lxcb-xv
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -8,4 +8,4 @@ Description: XCB XvMC Extension
Version: @PACKAGE_VERSION@
Requires.private: xcb xcb-xv
Libs: -L${libdir} -lxcb-xvmc
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@

View File

@ -10,4 +10,4 @@ Version: @PACKAGE_VERSION@
Requires.private: @NEEDED@
Libs: -L${libdir} -lxcb
Libs.private: @LIBS@
Cflags: -I${includedir}
Cflags: -I${includedir} @XCB_CFLAGS@