Commit 198a0039c5fca224a77e9761e2350dd9cc102ad0

Authored by Gerd Hoffmann
Committed by Anthony Liguori
1 parent 11a1feb6

vnc: rework VncState release workflow.

Split socket closing and releasing of VncState into two steps.  First
close the socket and set the variable to -1 to indicate shutdown in
progress.  Do the actual release in a few places where we can be sure it
doesn't cause trouble in form of use-after-free.  Add some checks for a
valid socket handle to make sure we don't try to use the closed socket.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing 1 changed file with 75 additions and 45 deletions
@@ -216,6 +216,8 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) { @@ -216,6 +216,8 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
216 */ 216 */
217 217
218 static void vnc_update_client(void *opaque); 218 static void vnc_update_client(void *opaque);
  219 +static void vnc_disconnect_start(VncState *vs);
  220 +static void vnc_disconnect_finish(VncState *vs);
219 221
220 static void vnc_colordepth(VncState *vs); 222 static void vnc_colordepth(VncState *vs);
221 223
@@ -652,9 +654,6 @@ static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) @@ -652,9 +654,6 @@ static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
652 654
653 static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) 655 static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
654 { 656 {
655 - vs->force_update = 1;  
656 - vnc_update_client(vs);  
657 -  
658 vnc_write_u8(vs, 0); /* msg id */ 657 vnc_write_u8(vs, 0); /* msg id */
659 vnc_write_u8(vs, 0); 658 vnc_write_u8(vs, 0);
660 vnc_write_u16(vs, 1); /* number of rects */ 659 vnc_write_u16(vs, 1); /* number of rects */
@@ -667,13 +666,22 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i @@ -667,13 +666,22 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i
667 static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) 666 static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
668 { 667 {
669 VncDisplay *vd = ds->opaque; 668 VncDisplay *vd = ds->opaque;
670 - VncState *vs = vd->clients;  
671 - while (vs != NULL) { 669 + VncState *vs, *vn;
  670 +
  671 + for (vs = vd->clients; vs != NULL; vs = vn) {
  672 + vn = vs->next;
  673 + if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
  674 + vs->force_update = 1;
  675 + vnc_update_client(vs);
  676 + /* vs might be free()ed here */
  677 + }
  678 + }
  679 +
  680 + for (vs = vd->clients; vs != NULL; vs = vs->next) {
672 if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) 681 if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT))
673 vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h); 682 vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
674 else /* TODO */ 683 else /* TODO */
675 vnc_update(vs, dst_x, dst_y, w, h); 684 vnc_update(vs, dst_x, dst_y, w, h);
676 - vs = vs->next;  
677 } 685 }
678 } 686 }
679 687
@@ -798,6 +806,8 @@ static void vnc_update_client(void *opaque) @@ -798,6 +806,8 @@ static void vnc_update_client(void *opaque)
798 806
799 if (vs->csock != -1) { 807 if (vs->csock != -1) {
800 qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); 808 qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
  809 + } else {
  810 + vnc_disconnect_finish(vs);
801 } 811 }
802 812
803 } 813 }
@@ -868,6 +878,48 @@ static void audio_del(VncState *vs) @@ -868,6 +878,48 @@ static void audio_del(VncState *vs)
868 } 878 }
869 } 879 }
870 880
  881 +static void vnc_disconnect_start(VncState *vs)
  882 +{
  883 + if (vs->csock == -1)
  884 + return;
  885 + qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
  886 + closesocket(vs->csock);
  887 + vs->csock = -1;
  888 +}
  889 +
  890 +static void vnc_disconnect_finish(VncState *vs)
  891 +{
  892 + qemu_del_timer(vs->timer);
  893 + qemu_free_timer(vs->timer);
  894 + if (vs->input.buffer) qemu_free(vs->input.buffer);
  895 + if (vs->output.buffer) qemu_free(vs->output.buffer);
  896 +#ifdef CONFIG_VNC_TLS
  897 + vnc_tls_client_cleanup(vs);
  898 +#endif /* CONFIG_VNC_TLS */
  899 +#ifdef CONFIG_VNC_SASL
  900 + vnc_sasl_client_cleanup(vs);
  901 +#endif /* CONFIG_VNC_SASL */
  902 + audio_del(vs);
  903 +
  904 + VncState *p, *parent = NULL;
  905 + for (p = vs->vd->clients; p != NULL; p = p->next) {
  906 + if (p == vs) {
  907 + if (parent)
  908 + parent->next = p->next;
  909 + else
  910 + vs->vd->clients = p->next;
  911 + break;
  912 + }
  913 + parent = p;
  914 + }
  915 + if (!vs->vd->clients)
  916 + dcl->idle = 1;
  917 +
  918 + qemu_free(vs->server.ds->data);
  919 + qemu_free(vs->server.ds);
  920 + qemu_free(vs->guest.ds);
  921 + qemu_free(vs);
  922 +}
871 923
872 int vnc_client_io_error(VncState *vs, int ret, int last_errno) 924 int vnc_client_io_error(VncState *vs, int ret, int last_errno)
873 { 925 {
@@ -885,39 +937,9 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) @@ -885,39 +937,9 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno)
885 } 937 }
886 } 938 }
887 939
888 - VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);  
889 - qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);  
890 - closesocket(vs->csock);  
891 - qemu_del_timer(vs->timer);  
892 - qemu_free_timer(vs->timer);  
893 - if (vs->input.buffer) qemu_free(vs->input.buffer);  
894 - if (vs->output.buffer) qemu_free(vs->output.buffer);  
895 -#ifdef CONFIG_VNC_TLS  
896 - vnc_tls_client_cleanup(vs);  
897 -#endif /* CONFIG_VNC_TLS */  
898 -#ifdef CONFIG_VNC_SASL  
899 - vnc_sasl_client_cleanup(vs);  
900 -#endif /* CONFIG_VNC_SASL */  
901 - audio_del(vs);  
902 -  
903 - VncState *p, *parent = NULL;  
904 - for (p = vs->vd->clients; p != NULL; p = p->next) {  
905 - if (p == vs) {  
906 - if (parent)  
907 - parent->next = p->next;  
908 - else  
909 - vs->vd->clients = p->next;  
910 - break;  
911 - }  
912 - parent = p;  
913 - }  
914 - if (!vs->vd->clients)  
915 - dcl->idle = 1;  
916 -  
917 - qemu_free(vs->server.ds->data);  
918 - qemu_free(vs->server.ds);  
919 - qemu_free(vs->guest.ds);  
920 - qemu_free(vs); 940 + VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
  941 + ret, ret < 0 ? last_errno : 0);
  942 + vnc_disconnect_start(vs);
921 943
922 return 0; 944 return 0;
923 } 945 }
@@ -927,7 +949,8 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) @@ -927,7 +949,8 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno)
927 949
928 void vnc_client_error(VncState *vs) 950 void vnc_client_error(VncState *vs)
929 { 951 {
930 - vnc_client_io_error(vs, -1, EINVAL); 952 + VNC_DEBUG("Closing down client sock: protocol error\n");
  953 + vnc_disconnect_start(vs);
931 } 954 }
932 955
933 956
@@ -1110,16 +1133,21 @@ void vnc_client_read(void *opaque) @@ -1110,16 +1133,21 @@ void vnc_client_read(void *opaque)
1110 else 1133 else
1111 #endif /* CONFIG_VNC_SASL */ 1134 #endif /* CONFIG_VNC_SASL */
1112 ret = vnc_client_read_plain(vs); 1135 ret = vnc_client_read_plain(vs);
1113 - if (!ret) 1136 + if (!ret) {
  1137 + if (vs->csock == -1)
  1138 + vnc_disconnect_finish(vs);
1114 return; 1139 return;
  1140 + }
1115 1141
1116 while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { 1142 while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
1117 size_t len = vs->read_handler_expect; 1143 size_t len = vs->read_handler_expect;
1118 int ret; 1144 int ret;
1119 1145
1120 ret = vs->read_handler(vs, vs->input.buffer, len); 1146 ret = vs->read_handler(vs, vs->input.buffer, len);
1121 - if (vs->csock == -1) 1147 + if (vs->csock == -1) {
  1148 + vnc_disconnect_finish(vs);
1122 return; 1149 return;
  1150 + }
1123 1151
1124 if (!ret) { 1152 if (!ret) {
1125 memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); 1153 memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
@@ -1134,7 +1162,7 @@ void vnc_write(VncState *vs, const void *data, size_t len) @@ -1134,7 +1162,7 @@ void vnc_write(VncState *vs, const void *data, size_t len)
1134 { 1162 {
1135 buffer_reserve(&vs->output, len); 1163 buffer_reserve(&vs->output, len);
1136 1164
1137 - if (buffer_empty(&vs->output)) { 1165 + if (vs->csock != -1 && buffer_empty(&vs->output)) {
1138 qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); 1166 qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
1139 } 1167 }
1140 1168
@@ -1175,7 +1203,7 @@ void vnc_write_u8(VncState *vs, uint8_t value) @@ -1175,7 +1203,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)
1175 1203
1176 void vnc_flush(VncState *vs) 1204 void vnc_flush(VncState *vs)
1177 { 1205 {
1178 - if (vs->output.offset) 1206 + if (vs->csock != -1 && vs->output.offset)
1179 vnc_client_write(vs); 1207 vnc_client_write(vs);
1180 } 1208 }
1181 1209
@@ -2009,11 +2037,13 @@ static void vnc_connect(VncDisplay *vd, int csock) @@ -2009,11 +2037,13 @@ static void vnc_connect(VncDisplay *vd, int csock)
2009 vnc_write(vs, "RFB 003.008\n", 12); 2037 vnc_write(vs, "RFB 003.008\n", 12);
2010 vnc_flush(vs); 2038 vnc_flush(vs);
2011 vnc_read_when(vs, protocol_version, 12); 2039 vnc_read_when(vs, protocol_version, 12);
2012 - vnc_update_client(vs);  
2013 reset_keys(vs); 2040 reset_keys(vs);
2014 2041
2015 vs->next = vd->clients; 2042 vs->next = vd->clients;
2016 vd->clients = vs; 2043 vd->clients = vs;
  2044 +
  2045 + vnc_update_client(vs);
  2046 + /* vs might be free()ed here */
2017 } 2047 }
2018 2048
2019 static void vnc_listen_read(void *opaque) 2049 static void vnc_listen_read(void *opaque)