diff --git a/configure.ac b/configure.ac index 4e6f028..67ed2de 100644 --- a/configure.ac +++ b/configure.ac @@ -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") diff --git a/src/Makefile.am b/src/Makefile.am index 9861a2d..9cc41c0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/c_client.py b/src/c_client.py index 730393e..345fba6 100644 --- a/src/c_client.py +++ b/src/c_client.py @@ -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 ') + _c('#include ') _c('#include ') _c('#include ') _c('#include /* 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 in the 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 diff --git a/src/xcb.h b/src/xcb.h index 59c779d..7195a49 100644 --- a/src/xcb.h +++ b/src/xcb.h @@ -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 */ diff --git a/src/xcb_conn.c b/src/xcb_conn.c index 8f91f43..d75f1a4 100644 --- a/src/xcb_conn.c +++ b/src/xcb_conn.c @@ -30,12 +30,14 @@ #endif #include +#include #include #include #include #include #include #include +#include #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. */ diff --git a/src/xcb_in.c b/src/xcb_in.c index c9a264d..541fb45 100644 --- a/src/xcb_in.c +++ b/src/xcb_in.c @@ -34,6 +34,7 @@ #include #include #include +#include #if USE_POLL #include @@ -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) { diff --git a/src/xcbext.h b/src/xcbext.h index 1bb992e..dad8bdd 100644 --- a/src/xcbext.h +++ b/src/xcbext.h @@ -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. * diff --git a/xcb-composite.pc.in b/xcb-composite.pc.in index b9b74c2..ebe577e 100644 --- a/xcb-composite.pc.in +++ b/xcb-composite.pc.in @@ -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@ diff --git a/xcb-damage.pc.in b/xcb-damage.pc.in index 3fb46b7..d89e683 100644 --- a/xcb-damage.pc.in +++ b/xcb-damage.pc.in @@ -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@ diff --git a/xcb-dbe.pc.in b/xcb-dbe.pc.in index 05df0d5..e06dcc6 100644 --- a/xcb-dbe.pc.in +++ b/xcb-dbe.pc.in @@ -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@ diff --git a/xcb-dpms.pc.in b/xcb-dpms.pc.in index 281861c..9c2a4ee 100644 --- a/xcb-dpms.pc.in +++ b/xcb-dpms.pc.in @@ -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@ diff --git a/xcb-dri2.pc.in b/xcb-dri2.pc.in index fdb188b..31256e9 100644 --- a/xcb-dri2.pc.in +++ b/xcb-dri2.pc.in @@ -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@ diff --git a/xcb-dri3.pc.in b/xcb-dri3.pc.in index befe11e..85c680f 100644 --- a/xcb-dri3.pc.in +++ b/xcb-dri3.pc.in @@ -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@ diff --git a/xcb-ge.pc.in b/xcb-ge.pc.in index b5f380d..6f80545 100644 --- a/xcb-ge.pc.in +++ b/xcb-ge.pc.in @@ -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@ diff --git a/xcb-glx.pc.in b/xcb-glx.pc.in index 79805ef..09c851a 100644 --- a/xcb-glx.pc.in +++ b/xcb-glx.pc.in @@ -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@ diff --git a/xcb-present.pc.in b/xcb-present.pc.in index 2459d1d..75340fc 100644 --- a/xcb-present.pc.in +++ b/xcb-present.pc.in @@ -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@ diff --git a/xcb-randr.pc.in b/xcb-randr.pc.in index 4c0de13..e58d12b 100644 --- a/xcb-randr.pc.in +++ b/xcb-randr.pc.in @@ -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@ diff --git a/xcb-record.pc.in b/xcb-record.pc.in index b441c9a..6f1b848 100644 --- a/xcb-record.pc.in +++ b/xcb-record.pc.in @@ -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@ diff --git a/xcb-render.pc.in b/xcb-render.pc.in index 9ad543e..0d6831f 100644 --- a/xcb-render.pc.in +++ b/xcb-render.pc.in @@ -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@ diff --git a/xcb-res.pc.in b/xcb-res.pc.in index 1f2889d..64963bd 100644 --- a/xcb-res.pc.in +++ b/xcb-res.pc.in @@ -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@ diff --git a/xcb-screensaver.pc.in b/xcb-screensaver.pc.in index 1209b20..86a2e17 100644 --- a/xcb-screensaver.pc.in +++ b/xcb-screensaver.pc.in @@ -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@ diff --git a/xcb-shape.pc.in b/xcb-shape.pc.in index 09637b4..180e81b 100644 --- a/xcb-shape.pc.in +++ b/xcb-shape.pc.in @@ -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@ diff --git a/xcb-shm.pc.in b/xcb-shm.pc.in index 47c193b..cbe30b7 100644 --- a/xcb-shm.pc.in +++ b/xcb-shm.pc.in @@ -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@ diff --git a/xcb-sync.pc.in b/xcb-sync.pc.in index 7a4c315..5eb5c02 100644 --- a/xcb-sync.pc.in +++ b/xcb-sync.pc.in @@ -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@ diff --git a/xcb-xevie.pc.in b/xcb-xevie.pc.in index dc4fee4..14a2575 100644 --- a/xcb-xevie.pc.in +++ b/xcb-xevie.pc.in @@ -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@ diff --git a/xcb-xf86dri.pc.in b/xcb-xf86dri.pc.in index 87c93d5..07c447d 100644 --- a/xcb-xf86dri.pc.in +++ b/xcb-xf86dri.pc.in @@ -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@ diff --git a/xcb-xfixes.pc.in b/xcb-xfixes.pc.in index 2ebb9cf..bad5206 100644 --- a/xcb-xfixes.pc.in +++ b/xcb-xfixes.pc.in @@ -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@ diff --git a/xcb-xinerama.pc.in b/xcb-xinerama.pc.in index abc3012..0337b2c 100644 --- a/xcb-xinerama.pc.in +++ b/xcb-xinerama.pc.in @@ -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@ diff --git a/xcb-xinput.pc.in b/xcb-xinput.pc.in index 1f1bb27..e6c1ec9 100644 --- a/xcb-xinput.pc.in +++ b/xcb-xinput.pc.in @@ -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@ diff --git a/xcb-xkb.pc.in b/xcb-xkb.pc.in index d5b1bad..7218748 100644 --- a/xcb-xkb.pc.in +++ b/xcb-xkb.pc.in @@ -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@ diff --git a/xcb-xprint.pc.in b/xcb-xprint.pc.in index 7ac65f6..d6f81fb 100644 --- a/xcb-xprint.pc.in +++ b/xcb-xprint.pc.in @@ -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@ diff --git a/xcb-xselinux.pc.in b/xcb-xselinux.pc.in index 0f86a93..1319c02 100644 --- a/xcb-xselinux.pc.in +++ b/xcb-xselinux.pc.in @@ -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@ diff --git a/xcb-xtest.pc.in b/xcb-xtest.pc.in index 9961152..a452f23 100644 --- a/xcb-xtest.pc.in +++ b/xcb-xtest.pc.in @@ -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@ diff --git a/xcb-xv.pc.in b/xcb-xv.pc.in index f31de1a..6adc91d 100644 --- a/xcb-xv.pc.in +++ b/xcb-xv.pc.in @@ -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@ diff --git a/xcb-xvmc.pc.in b/xcb-xvmc.pc.in index 93c51a9..226a2d8 100644 --- a/xcb-xvmc.pc.in +++ b/xcb-xvmc.pc.in @@ -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@ diff --git a/xcb.pc.in b/xcb.pc.in index 2dc8c13..4cdee23 100644 --- a/xcb.pc.in +++ b/xcb.pc.in @@ -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@