From e0f76d00979c972329f6c371463a20da61ccad65 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 9 May 2016 20:38:53 +0200 Subject: patch 7.4.1828 Problem: May try to access buffer that's already freed. Solution: When freeing a buffer remove it from any channel. --- src/buffer.c | 3 +++ src/channel.c | 67 ++++++++++++++++++++++++++++++++++++++++----------- src/proto/channel.pro | 1 + src/version.c | 2 ++ 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 9bc24bc12..e884f55dd 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -676,6 +676,9 @@ free_buffer(buf_T *buf) #ifdef FEAT_RUBY ruby_buffer_free(buf); #endif +#ifdef FEAT_JOB_CHANNEL + channel_buffer_free(buf); +#endif #ifdef FEAT_AUTOCMD aubuflocal_remove(buf); if (autocmd_busy) diff --git a/src/channel.c b/src/channel.c index a7f4c4b8c..c191c2a0f 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1068,6 +1068,7 @@ channel_set_job(channel_T *channel, job_T *job, jobopt_T *options) /* * Find a buffer matching "name" or create a new one. + * Returns NULL if there is something very wrong (error already reported). */ static buf_T * find_buffer(char_u *name, int err) @@ -1081,6 +1082,8 @@ find_buffer(char_u *name, int err) { buf = buflist_new(name == NULL || *name == NUL ? NULL : name, NULL, (linenr_T)0, BLN_LISTED); + if (buf == NULL) + return NULL; buf_copy_options(buf, BCO_ENTER); curbuf = buf; #ifdef FEAT_QUICKFIX @@ -1187,37 +1190,54 @@ channel_set_options(channel_T *channel, jobopt_T *opt) if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER) { + buf_T *buf; + /* writing output to a buffer. Default mode is NL. */ if (!(opt->jo_set & JO_OUT_MODE)) channel->ch_part[PART_OUT].ch_mode = MODE_NL; if (opt->jo_set & JO_OUT_BUF) - channel->ch_part[PART_OUT].ch_buffer = - buflist_findnr(opt->jo_io_buf[PART_OUT]); + { + buf = buflist_findnr(opt->jo_io_buf[PART_OUT]); + if (buf == NULL) + EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[PART_OUT]); + } else - channel->ch_part[PART_OUT].ch_buffer = - find_buffer(opt->jo_io_name[PART_OUT], FALSE); - ch_logs(channel, "writing out to buffer '%s'", - (char *)channel->ch_part[PART_OUT].ch_buffer->b_ffname); + { + buf = find_buffer(opt->jo_io_name[PART_OUT], FALSE); + } + if (buf != NULL) + { + ch_logs(channel, "writing out to buffer '%s'", + (char *)buf->b_ffname); + channel->ch_part[PART_OUT].ch_buffer = buf; + } } if ((opt->jo_set & JO_ERR_IO) && (opt->jo_io[PART_ERR] == JIO_BUFFER || (opt->jo_io[PART_ERR] == JIO_OUT && (opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER))) { + buf_T *buf; + /* writing err to a buffer. Default mode is NL. */ if (!(opt->jo_set & JO_ERR_MODE)) channel->ch_part[PART_ERR].ch_mode = MODE_NL; if (opt->jo_io[PART_ERR] == JIO_OUT) - channel->ch_part[PART_ERR].ch_buffer = - channel->ch_part[PART_OUT].ch_buffer; + buf = channel->ch_part[PART_OUT].ch_buffer; else if (opt->jo_set & JO_ERR_BUF) - channel->ch_part[PART_ERR].ch_buffer = - buflist_findnr(opt->jo_io_buf[PART_ERR]); + { + buf = buflist_findnr(opt->jo_io_buf[PART_ERR]); + if (buf == NULL) + EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[PART_ERR]); + } else - channel->ch_part[PART_ERR].ch_buffer = - find_buffer(opt->jo_io_name[PART_ERR], TRUE); - ch_logs(channel, "writing err to buffer '%s'", - (char *)channel->ch_part[PART_ERR].ch_buffer->b_ffname); + buf = find_buffer(opt->jo_io_name[PART_ERR], TRUE); + if (buf != NULL) + { + ch_logs(channel, "writing err to buffer '%s'", + (char *)buf->b_ffname); + channel->ch_part[PART_ERR].ch_buffer = buf; + } } channel->ch_part[PART_OUT].ch_io = opt->jo_io[PART_OUT]; @@ -1387,6 +1407,25 @@ channel_write_in(channel_T *channel) buf->b_ml.ml_line_count - lnum + 1); } +/* + * Handle buffer "buf" beeing freed, remove it from any channels. + */ + void +channel_buffer_free(buf_T *buf) +{ + channel_T *channel; + int part; + + for (channel = first_channel; channel != NULL; channel = channel->ch_next) + for (part = PART_SOCK; part <= PART_IN; ++part) + { + chanpart_T *ch_part = &channel->ch_part[part]; + + if (ch_part->ch_buffer == buf) + ch_part->ch_buffer = NULL; + } +} + /* * Write any lines waiting to be written to a channel. */ diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 5e0bec54b..5dc512181 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -14,6 +14,7 @@ void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options); void channel_set_options(channel_T *channel, jobopt_T *opt); void channel_set_req_callback(channel_T *channel, int part, char_u *callback, partial_T *partial, int id); +void channel_buffer_free(buf_T *buf); void channel_write_any_lines(void); void channel_write_new_lines(buf_T *buf); char_u *channel_get(channel_T *channel, int part); diff --git a/src/version.c b/src/version.c index 266e59148..fb3b3d48b 100644 --- a/src/version.c +++ b/src/version.c @@ -753,6 +753,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1828, /**/ 1827, /**/ -- cgit v1.2.1