aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2019-01-15 23:51:45 -0800
committerPaul Eggert2019-01-15 23:52:47 -0800
commite87e6a24c49542111e669b7d0f1a412024663f8e (patch)
treeba382d326874795c7b131a7995c67e660650a80c /src
parent6b9fa8804533a695094a930d634d2d6617e2b6c7 (diff)
downloademacs-e87e6a24c49542111e669b7d0f1a412024663f8e.tar.gz
emacs-e87e6a24c49542111e669b7d0f1a412024663f8e.zip
Fix unlikely races with GnuTLS, datagrams
Retry some calls if interrupted at inopportune times. These were found by code inspection. * src/gnutls.c (gnutls_try_handshake): Simplify by using new emacs_gnutls_handle_error API. (emacs_gnutls_write): Remove GNUTLS_E_AGAIN hack since emacs_gnutls_handle_error now does that. Use emacs_gnutls_handle_error only on errors. (emacs_gnutls_read): Retry if gnutls_record_recv returns GNUTLS_E_INTERRUPTED, to be consistent with emacs_read. (emacs_gnutls_handle_error): Return 0 on fatal errors, -1 (setting errno) on ordinary errors, to simplify callers. Assume that ERR is negative, since it always is now. Map non-fatal GnuTLS errors to errno values as best we can. * src/process.c (read_process_output) [DATAGRAM_SOCKETS]: Retry recvfrom if it is interrupted, to be consistent with how things are handled when not a datagram channel. (send_process) [DATAGRAM_SOCEKTS]: If sendto is interrupted, process pending signals and retry it, to be consistent with how things are handled when not a datagram channel.
Diffstat (limited to 'src')
-rw-r--r--src/gnutls.c96
-rw-r--r--src/process.c28
2 files changed, 74 insertions, 50 deletions
diff --git a/src/gnutls.c b/src/gnutls.c
index d0cb28dc536..63dbcf4162b 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -72,7 +72,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
72# include "w32.h" 72# include "w32.h"
73# endif 73# endif
74 74
75static bool emacs_gnutls_handle_error (gnutls_session_t, int); 75static int emacs_gnutls_handle_error (gnutls_session_t, int);
76 76
77static bool gnutls_global_initialized; 77static bool gnutls_global_initialized;
78 78
@@ -579,15 +579,17 @@ gnutls_try_handshake (struct Lisp_Process *proc)
579 if (non_blocking) 579 if (non_blocking)
580 proc->gnutls_p = true; 580 proc->gnutls_p = true;
581 581
582 do 582 while ((ret = gnutls_handshake (state)) < 0)
583 { 583 {
584 ret = gnutls_handshake (state); 584 do
585 emacs_gnutls_handle_error (state, ret); 585 ret = gnutls_handshake (state);
586 while (ret == GNUTLS_E_INTERRUPTED);
587
588 if (0 <= ret || emacs_gnutls_handle_error (state, ret) == 0
589 || non_blocking)
590 break;
586 maybe_quit (); 591 maybe_quit ();
587 } 592 }
588 while (ret < 0
589 && gnutls_error_is_fatal (ret) == 0
590 && ! non_blocking);
591 593
592 proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED; 594 proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
593 595
@@ -682,8 +684,6 @@ emacs_gnutls_transport_set_errno (gnutls_session_t state, int err)
682ptrdiff_t 684ptrdiff_t
683emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte) 685emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte)
684{ 686{
685 ssize_t rtnval = 0;
686 ptrdiff_t bytes_written;
687 gnutls_session_t state = proc->gnutls_state; 687 gnutls_session_t state = proc->gnutls_state;
688 688
689 if (proc->gnutls_initstage != GNUTLS_STAGE_READY) 689 if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
@@ -692,25 +692,19 @@ emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte)
692 return 0; 692 return 0;
693 } 693 }
694 694
695 bytes_written = 0; 695 ptrdiff_t bytes_written = 0;
696 696
697 while (nbyte > 0) 697 while (nbyte > 0)
698 { 698 {
699 rtnval = gnutls_record_send (state, buf, nbyte); 699 ssize_t rtnval;
700 do
701 rtnval = gnutls_record_send (state, buf, nbyte);
702 while (rtnval == GNUTLS_E_INTERRUPTED);
700 703
701 if (rtnval < 0) 704 if (rtnval < 0)
702 { 705 {
703 if (rtnval == GNUTLS_E_INTERRUPTED) 706 emacs_gnutls_handle_error (state, rtnval);
704 continue; 707 break;
705 else
706 {
707 /* If we get GNUTLS_E_AGAIN, then set errno
708 appropriately so that send_process retries the
709 correct way instead of erroring out. */
710 if (rtnval == GNUTLS_E_AGAIN)
711 errno = EAGAIN;
712 break;
713 }
714 } 708 }
715 709
716 buf += rtnval; 710 buf += rtnval;
@@ -718,14 +712,12 @@ emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte)
718 bytes_written += rtnval; 712 bytes_written += rtnval;
719 } 713 }
720 714
721 emacs_gnutls_handle_error (state, rtnval);
722 return (bytes_written); 715 return (bytes_written);
723} 716}
724 717
725ptrdiff_t 718ptrdiff_t
726emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) 719emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte)
727{ 720{
728 ssize_t rtnval;
729 gnutls_session_t state = proc->gnutls_state; 721 gnutls_session_t state = proc->gnutls_state;
730 722
731 if (proc->gnutls_initstage != GNUTLS_STAGE_READY) 723 if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
@@ -734,19 +726,18 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte)
734 return -1; 726 return -1;
735 } 727 }
736 728
737 rtnval = gnutls_record_recv (state, buf, nbyte); 729 ssize_t rtnval;
730 do
731 rtnval = gnutls_record_recv (state, buf, nbyte);
732 while (rtnval == GNUTLS_E_INTERRUPTED);
733
738 if (rtnval >= 0) 734 if (rtnval >= 0)
739 return rtnval; 735 return rtnval;
740 else if (rtnval == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) 736 else if (rtnval == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
741 /* The peer closed the connection. */ 737 /* The peer closed the connection. */
742 return 0; 738 return 0;
743 else if (emacs_gnutls_handle_error (state, rtnval)) 739 else
744 /* non-fatal error */ 740 return emacs_gnutls_handle_error (state, rtnval);
745 return -1;
746 else {
747 /* a fatal error occurred */
748 return 0;
749 }
750} 741}
751 742
752static char const * 743static char const *
@@ -757,25 +748,24 @@ emacs_gnutls_strerror (int err)
757} 748}
758 749
759/* Report a GnuTLS error to the user. 750/* Report a GnuTLS error to the user.
760 Return true if the error code was successfully handled. */ 751 SESSION is the GnuTLS session, ERR is the (negative) GnuTLS error code.
761static bool 752 Return 0 if the error was fatal, -1 (setting errno) otherwise so
753 that the caller can notice the error and attempt a repair. */
754static int
762emacs_gnutls_handle_error (gnutls_session_t session, int err) 755emacs_gnutls_handle_error (gnutls_session_t session, int err)
763{ 756{
764 int max_log_level = 0; 757 int ret;
765
766 bool ret;
767 758
768 /* TODO: use a Lisp_Object generated by gnutls_make_error? */ 759 /* TODO: use a Lisp_Object generated by gnutls_make_error? */
769 if (err >= 0)
770 return 1;
771 760
772 check_memory_full (err); 761 check_memory_full (err);
773 762
774 max_log_level = global_gnutls_log_level; 763 int max_log_level = global_gnutls_log_level;
775 764
776 /* TODO: use gnutls-error-fatalp and gnutls-error-string. */ 765 /* TODO: use gnutls-error-fatalp and gnutls-error-string. */
777 766
778 char const *str = emacs_gnutls_strerror (err); 767 char const *str = emacs_gnutls_strerror (err);
768 int errnum = EINVAL;
779 769
780 if (gnutls_error_is_fatal (err)) 770 if (gnutls_error_is_fatal (err))
781 { 771 {
@@ -789,11 +779,11 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err)
789# endif 779# endif
790 780
791 GNUTLS_LOG2 (level, max_log_level, "fatal error:", str); 781 GNUTLS_LOG2 (level, max_log_level, "fatal error:", str);
792 ret = false; 782 ret = 0;
793 } 783 }
794 else 784 else
795 { 785 {
796 ret = true; 786 ret = -1;
797 787
798 switch (err) 788 switch (err)
799 { 789 {
@@ -809,6 +799,26 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err)
809 "non-fatal error:", 799 "non-fatal error:",
810 str); 800 str);
811 } 801 }
802
803 switch (err)
804 {
805 case GNUTLS_E_AGAIN:
806 errnum = EAGAIN;
807 break;
808
809# ifdef EMSGSIZE
810 case GNUTLS_E_LARGE_PACKET:
811 case GNUTLS_E_PUSH_ERROR:
812 errnum = EMSGSIZE;
813 break;
814# endif
815
816# if defined HAVE_GNUTLS3 && defined ECONNRESET
817 case GNUTLS_E_PREMATURE_TERMINATION:
818 errnum = ECONNRESET;
819 break;
820# endif
821 }
812 } 822 }
813 823
814 if (err == GNUTLS_E_WARNING_ALERT_RECEIVED 824 if (err == GNUTLS_E_WARNING_ALERT_RECEIVED
@@ -822,6 +832,8 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err)
822 832
823 GNUTLS_LOG2 (level, max_log_level, "Received alert: ", str); 833 GNUTLS_LOG2 (level, max_log_level, "Received alert: ", str);
824 } 834 }
835
836 errno = errnum;
825 return ret; 837 return ret;
826} 838}
827 839
diff --git a/src/process.c b/src/process.c
index 06555bac4c0..c0741403b57 100644
--- a/src/process.c
+++ b/src/process.c
@@ -5840,7 +5840,8 @@ read_and_dispose_of_process_output (struct Lisp_Process *p, char *chars,
5840 5840
5841/* Read pending output from the process channel, 5841/* Read pending output from the process channel,
5842 starting with our buffered-ahead character if we have one. 5842 starting with our buffered-ahead character if we have one.
5843 Yield number of decoded characters read. 5843 Yield number of decoded characters read,
5844 or -1 (setting errno) if there is a read error.
5844 5845
5845 This function reads at most 4096 characters. 5846 This function reads at most 4096 characters.
5846 If you want to read all available subprocess output, 5847 If you want to read all available subprocess output,
@@ -5870,8 +5871,10 @@ read_process_output (Lisp_Object proc, int channel)
5870 if (DATAGRAM_CHAN_P (channel)) 5871 if (DATAGRAM_CHAN_P (channel))
5871 { 5872 {
5872 socklen_t len = datagram_address[channel].len; 5873 socklen_t len = datagram_address[channel].len;
5873 nbytes = recvfrom (channel, chars + carryover, readmax, 5874 do
5874 0, datagram_address[channel].sa, &len); 5875 nbytes = recvfrom (channel, chars + carryover, readmax,
5876 0, datagram_address[channel].sa, &len);
5877 while (nbytes < 0 && errno == EINTR);
5875 } 5878 }
5876 else 5879 else
5877#endif 5880#endif
@@ -5921,8 +5924,6 @@ read_process_output (Lisp_Object proc, int channel)
5921 5924
5922 p->decoding_carryover = 0; 5925 p->decoding_carryover = 0;
5923 5926
5924 /* At this point, NBYTES holds number of bytes just received
5925 (including the one in proc_buffered_char[channel]). */
5926 if (nbytes <= 0) 5927 if (nbytes <= 0)
5927 { 5928 {
5928 if (nbytes < 0 || coding->mode & CODING_MODE_LAST_BLOCK) 5929 if (nbytes < 0 || coding->mode & CODING_MODE_LAST_BLOCK)
@@ -5930,6 +5931,9 @@ read_process_output (Lisp_Object proc, int channel)
5930 coding->mode |= CODING_MODE_LAST_BLOCK; 5931 coding->mode |= CODING_MODE_LAST_BLOCK;
5931 } 5932 }
5932 5933
5934 /* At this point, NBYTES holds number of bytes just received
5935 (including the one in proc_buffered_char[channel]). */
5936
5933 /* Ignore carryover, it's been added by a previous iteration already. */ 5937 /* Ignore carryover, it's been added by a previous iteration already. */
5934 p->nbytes_read += nbytes; 5938 p->nbytes_read += nbytes;
5935 5939
@@ -6372,9 +6376,17 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
6372#ifdef DATAGRAM_SOCKETS 6376#ifdef DATAGRAM_SOCKETS
6373 if (DATAGRAM_CHAN_P (outfd)) 6377 if (DATAGRAM_CHAN_P (outfd))
6374 { 6378 {
6375 rv = sendto (outfd, cur_buf, cur_len, 6379 while (true)
6376 0, datagram_address[outfd].sa, 6380 {
6377 datagram_address[outfd].len); 6381 rv = sendto (outfd, cur_buf, cur_len, 0,
6382 datagram_address[outfd].sa,
6383 datagram_address[outfd].len);
6384 if (! (rv < 0 && errno == EINTR))
6385 break;
6386 if (pending_signals)
6387 process_pending_signals ();
6388 }
6389
6378 if (rv >= 0) 6390 if (rv >= 0)
6379 written = rv; 6391 written = rv;
6380 else if (errno == EMSGSIZE) 6392 else if (errno == EMSGSIZE)