From 52e6c900c3fd044e7ec8cf6f25af7a588dfc5776 Mon Sep 17 00:00:00 2001 From: Andrew Hlynskyi Date: Fri, 3 Sep 2021 12:49:42 +0300 Subject: [PATCH] fix(lib): fix segfault on ts_query_new with incompatible grammar version, close #1318 --- cli/loader/src/lib.rs | 49 ++++++++++++++++++---------------- lib/binding_rust/bindings.rs | 1 + lib/binding_rust/lib.rs | 50 ++++++++++++++++++++++++----------- lib/include/tree_sitter/api.h | 1 + lib/src/query.c | 9 +++++++ 5 files changed, 72 insertions(+), 38 deletions(-) diff --git a/cli/loader/src/lib.rs b/cli/loader/src/lib.rs index 89018677..d1a37df0 100644 --- a/cli/loader/src/lib.rs +++ b/cli/loader/src/lib.rs @@ -12,7 +12,7 @@ use std::process::Command; use std::sync::Mutex; use std::time::SystemTime; use std::{fs, mem}; -use tree_sitter::{Language, QueryError}; +use tree_sitter::{Language, QueryError, QueryErrorKind}; use tree_sitter_highlight::HighlightConfiguration; use tree_sitter_tags::{Error as TagsError, TagsConfiguration}; @@ -667,28 +667,31 @@ impl<'a> LanguageConfiguration<'a> { &injections_query, &locals_query, ) - .map_err(|error| { - if error.offset < injections_query.len() { - Self::include_path_in_query_error( - error, - &injection_ranges, - &injections_query, - 0, - ) - } else if error.offset < injections_query.len() + locals_query.len() { - Self::include_path_in_query_error( - error, - &locals_ranges, - &locals_query, - injections_query.len(), - ) - } else { - Self::include_path_in_query_error( - error, - &highlight_ranges, - &highlights_query, - injections_query.len() + locals_query.len(), - ) + .map_err(|error| match error.kind { + QueryErrorKind::Language => Error::from(error), + _ => { + if error.offset < injections_query.len() { + Self::include_path_in_query_error( + error, + &injection_ranges, + &injections_query, + 0, + ) + } else if error.offset < injections_query.len() + locals_query.len() { + Self::include_path_in_query_error( + error, + &locals_ranges, + &locals_query, + injections_query.len(), + ) + } else { + Self::include_path_in_query_error( + error, + &highlight_ranges, + &highlights_query, + injections_query.len() + locals_query.len(), + ) + } } })?; let mut all_highlight_names = self.highlight_names.lock().unwrap(); diff --git a/lib/binding_rust/bindings.rs b/lib/binding_rust/bindings.rs index 5c032a36..881780e4 100644 --- a/lib/binding_rust/bindings.rs +++ b/lib/binding_rust/bindings.rs @@ -133,6 +133,7 @@ pub const TSQueryError_TSQueryErrorNodeType: TSQueryError = 2; pub const TSQueryError_TSQueryErrorField: TSQueryError = 3; pub const TSQueryError_TSQueryErrorCapture: TSQueryError = 4; pub const TSQueryError_TSQueryErrorStructure: TSQueryError = 5; +pub const TSQueryError_TSQueryErrorLanguage: TSQueryError = 6; pub type TSQueryError = u32; extern "C" { #[doc = " Create a new parser."] diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 08dd7b11..57f678d8 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -202,6 +202,7 @@ pub enum QueryErrorKind { Capture, Predicate, Structure, + Language, } #[derive(Debug)] @@ -1231,6 +1232,19 @@ impl Query { // On failure, build an error based on the error code and offset. if ptr.is_null() { + if error_type == ffi::TSQueryError_TSQueryErrorLanguage { + return Err(QueryError { + row: 0, + column: 0, + offset: 0, + message: LanguageError { + version: language.version(), + } + .to_string(), + kind: QueryErrorKind::Language, + }); + } + let offset = error_offset as usize; let mut line_start = 0; let mut row = 0; @@ -2105,21 +2119,27 @@ impl fmt::Display for LanguageError { impl fmt::Display for QueryError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Query error at {}:{}. {}{}", - self.row + 1, - self.column + 1, - match self.kind { - QueryErrorKind::Field => "Invalid field name ", - QueryErrorKind::NodeType => "Invalid node type ", - QueryErrorKind::Capture => "Invalid capture name ", - QueryErrorKind::Predicate => "Invalid predicate: ", - QueryErrorKind::Structure => "Impossible pattern:\n", - QueryErrorKind::Syntax => "Invalid syntax:\n", - }, - self.message - ) + let msg = match self.kind { + QueryErrorKind::Field => "Invalid field name ", + QueryErrorKind::NodeType => "Invalid node type ", + QueryErrorKind::Capture => "Invalid capture name ", + QueryErrorKind::Predicate => "Invalid predicate: ", + QueryErrorKind::Structure => "Impossible pattern:\n", + QueryErrorKind::Syntax => "Invalid syntax:\n", + QueryErrorKind::Language => "", + }; + if msg.len() > 0 { + write!( + f, + "Query error at {}:{}. {}{}", + self.row + 1, + self.column + 1, + msg, + self.message + ) + } else { + write!(f, "{}", self.message) + } } } diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index f02789ee..e4d49a58 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -131,6 +131,7 @@ typedef enum { TSQueryErrorField, TSQueryErrorCapture, TSQueryErrorStructure, + TSQueryErrorLanguage, } TSQueryError; /********************/ diff --git a/lib/src/query.c b/lib/src/query.c index 2e8e4b79..1e6ba848 100644 --- a/lib/src/query.c +++ b/lib/src/query.c @@ -2069,6 +2069,15 @@ TSQuery *ts_query_new( uint32_t *error_offset, TSQueryError *error_type ) { + if ( + !language || + language->version > TREE_SITTER_LANGUAGE_VERSION || + language->version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION + ) { + *error_type = TSQueryErrorLanguage; + return NULL; + } + TSQuery *self = ts_malloc(sizeof(TSQuery)); *self = (TSQuery) { .steps = array_new(),