@@ -52,19 +52,18 @@ extern bool reset_msg_stack;
5252
5353static CONFIDENTIAL char strbuf [BODY_CHAR_MAX ];
5454
55- /* Set by format_body() when the formatted body did not fit strbuf, i.e. when
56- * characters were lost before any screen existed to show them. Read and
57- * cleared by confirm_helper(). Truncation here is invisible to every later
58- * check: what reaches the renderer is a complete, well-formed, shorter string,
59- * so the screen looks correct and is not. */
60- static bool body_truncated = false;
61-
62- /* The single place a host-supplied body is formatted. vsnprintf() returns the
63- * length it WOULD have written, which is the only chance to notice that
64- * strbuf was too small -- after this, the evidence is gone. */
65- static void format_body (const char * request_body , va_list vl ) {
66- const int needed = vsnprintf (strbuf , sizeof (strbuf ), request_body , vl );
67- body_truncated = (needed < 0 ) || ((size_t )needed >= sizeof (strbuf ));
55+ /* vsnprintf() returns the length it WOULD have written. Treat anything that
56+ * did not fit as a refusal: once characters are lost, no renderer or pager can
57+ * recover them and there is no complete body the user can approve. */
58+ static bool format_body_into (char * out , size_t out_len ,
59+ const char * request_body , va_list vl ) {
60+ if (!out || out_len == 0 || !request_body ) return false;
61+ const int needed = vsnprintf (out , out_len , request_body , vl );
62+ return needed >= 0 && (size_t )needed < out_len ;
63+ }
64+
65+ static bool format_body (const char * request_body , va_list vl ) {
66+ return format_body_into (strbuf , sizeof (strbuf ), request_body , vl );
6867}
6968
7069/// Handler for push button being pressed.
@@ -511,19 +510,14 @@ static bool page_body_confirm(const char* request_title, const char* body,
511510 return ok ;
512511}
513512
514- /// Show a confirmation, warning first when its body will not fit the screen.
513+ /// Show a confirmation, paging when its complete body will not fit the screen.
515514///
516515/// draw_string() draws until a glyph no longer fits the canvas and then simply
517516/// stops: a body taller than BODY_ROWS is drawn in part, with no ellipsis and
518517/// nothing to tell the user that the tail of an address, an amount or a
519- /// warning was dropped. The vsnprintf() into strbuf[BODY_CHAR_MAX] below cuts
520- /// long host strings a second time, just as quietly.
521- ///
522- /// So when the body will not fit, put an explicit screen in front of it. That
523- /// screen costs its own hold, and the hold is a real consent signal: a host
524- /// Cancel breaks it and the caller reports ActionCancelled, exactly as it
525- /// would for the body screen. A body that is only partly shown is now never
526- /// shown without saying so.
518+ /// warning was dropped. Complete formatted bodies are therefore paged here.
519+ /// Source formatting overflow is refused by every public entry point before a
520+ /// ButtonRequest is emitted, because lost source cannot be paged.
527521///
528522/// Bodies that fit take exactly the path they took before: one screen, one
529523/// ButtonRequest, one hold.
@@ -534,70 +528,15 @@ static bool confirm_helper(const char* request_title, const char* request_body,
534528 const uint16_t body_width =
535529 (uint16_t )((iconNum == NO_ICON ) ? BODY_WIDTH : BODY_WIDTH_WITH_ICON );
536530
537- /* Consume the source-completeness latch exactly once, whatever happens
538- * below: leaving it set would make the NEXT confirmation warn for this
539- * one's reason. */
540- const bool truncated = body_truncated ;
541- body_truncated = false;
542-
543- /* Two independent ways the user can be shown less than what is being
544- * approved, and they need separate measurements because they happen at
545- * different times:
546- *
547- * SOURCE the formatted body did not fit strbuf. Characters were lost
548- * before the renderer ever saw them, so no amount of looking
549- * at the screen can detect it -- only vsnprintf()'s return
550- * value could, and format_body() kept it.
551- * RENDER the body reached the renderer intact but did not fit the
552- * canvas. draw_string_fits() replays the real placement and
553- * reports whether the last character landed.
554- *
555- * Only layout_standard_notification is known to wrap the body at BODY_WIDTH
531+ /* Only layout_standard_notification is known to wrap the body at BODY_WIDTH
556532 * over BODY_ROWS rows. Custom layouts place and size their own body, and
557533 * layout_constant_power_notification draws from x = 128 + LEFT_MARGIN where
558534 * the canvas edge, not BODY_WIDTH, is the limit. Measuring either of those
559- * against BODY_WIDTH would be wrong, so leave them exactly as they were --
560- * but a SOURCE truncation is layout-independent and must warn regardless. */
535+ * against BODY_WIDTH would be wrong, so leave them exactly as they were. */
561536 const bool render_incomplete =
562537 (layout_notification_func == & layout_standard_notification ) &&
563538 !confirm_body_fits (request_body , body_width );
564539
565- if (truncated ) {
566- /* SOURCE truncation: characters were lost in vsnprintf() before the
567- * renderer ever saw them. They cannot be paged, because they do not
568- * exist any more. Say exactly that -- the old copy promised to show the
569- * rest on the next hold and then redrew the same clipped body, which is
570- * worse than not warning at all: a user who read it carefully was
571- * misled about what they had seen. */
572- if (!confirm_screen ("Cut Off" ,
573- "This text is too long to show in full. The rest "
574- "cannot be displayed. Hold to continue anyway." ,
575- & layout_standard_notification , constant_power , NO_ICON ,
576- immediate )) {
577- return false;
578- }
579- /* The warning CONSUMED the caller's ButtonRequest, so the body screen that
580- * follows needs one of its own -- otherwise it is a required press the
581- * host was never told about, and the invariant above is broken on exactly
582- * the path that added a screen. An interactive user would get through it
583- * (button_request_acked is still set from the warning's ack), but a
584- * ButtonRequest-driven client waits forever for a message that never
585- * comes. Reachable today: confirm_sign_identity() formats into a 416-byte
586- * body and strbuf is BODY_CHAR_MAX == 352, so a long identity truncates
587- * and lands here. See #482. */
588- if (notify_host ) {
589- ButtonRequest body_req ;
590- memset (& body_req , 0 , sizeof (body_req ));
591- body_req .has_code = true;
592- body_req .code = ButtonRequestType_ButtonRequest_Other ;
593- button_request_acked = false;
594- msg_write (MessageType_MessageType_ButtonRequest , & body_req );
595- }
596- return page_body_confirm (request_title , request_body ,
597- layout_notification_func , constant_power , iconNum ,
598- immediate , body_width , notify_host );
599- }
600-
601540 if (render_incomplete ) {
602541 /* RENDER overflow: the body reached the renderer intact, so every
603542 * character is still in hand and can be shown -- on more than one screen.
@@ -617,8 +556,12 @@ bool confirm(ButtonRequestType type, const char* request_title,
617556
618557 va_list vl ;
619558 va_start (vl , request_body );
620- format_body (request_body , vl );
559+ const bool formatted = format_body (request_body , vl );
621560 va_end (vl );
561+ if (!formatted ) {
562+ memzero (strbuf , sizeof (strbuf ));
563+ return false;
564+ }
622565
623566 /* Send button request */
624567 ButtonRequest resp ;
@@ -716,8 +659,12 @@ bool confirm_constant_power(ButtonRequestType type, const char* request_title,
716659
717660 va_list vl ;
718661 va_start (vl , request_body );
719- format_body (request_body , vl );
662+ const bool formatted = format_body (request_body , vl );
720663 va_end (vl );
664+ if (!formatted ) {
665+ memzero (strbuf , sizeof (strbuf ));
666+ return false;
667+ }
721668
722669 /* Send button request */
723670 ButtonRequest resp ;
@@ -740,8 +687,12 @@ bool confirm_with_custom_button_request(const ButtonRequest* button_request,
740687
741688 va_list vl ;
742689 va_start (vl , request_body );
743- format_body (request_body , vl );
690+ const bool formatted = format_body (request_body , vl );
744691 va_end (vl );
692+ if (!formatted ) {
693+ memzero (strbuf , sizeof (strbuf ));
694+ return false;
695+ }
745696
746697 /* Send button request */
747698 msg_write (MessageType_MessageType_ButtonRequest , button_request );
@@ -766,8 +717,12 @@ bool confirm_with_custom_layout(layout_notification_t layout_notification_func,
766717
767718 va_list vl ;
768719 va_start (vl , request_body );
769- format_body (request_body , vl );
720+ const bool formatted = format_body (request_body , vl );
770721 va_end (vl );
722+ if (!formatted ) {
723+ memzero (strbuf , sizeof (strbuf ));
724+ return false;
725+ }
771726
772727 /* Send button request */
773728 ButtonRequest resp ;
@@ -789,8 +744,12 @@ bool confirm_without_button_request(const char* request_title,
789744
790745 va_list vl ;
791746 va_start (vl , request_body );
792- format_body (request_body , vl );
747+ const bool formatted = format_body (request_body , vl );
793748 va_end (vl );
749+ if (!formatted ) {
750+ memzero (strbuf , sizeof (strbuf ));
751+ return false;
752+ }
794753
795754 bool ret =
796755 confirm_helper (request_title , strbuf , & layout_standard_notification ,
@@ -806,8 +765,12 @@ bool confirm_with_icon(ButtonRequestType type, IconType iconNum,
806765
807766 va_list vl ;
808767 va_start (vl , request_body );
809- format_body (request_body , vl );
768+ const bool formatted = format_body (request_body , vl );
810769 va_end (vl );
770+ if (!formatted ) {
771+ memzero (strbuf , sizeof (strbuf ));
772+ return false;
773+ }
811774
812775 /* Send button request */
813776 ButtonRequest resp ;
@@ -829,8 +792,12 @@ bool review(ButtonRequestType type, const char* request_title,
829792
830793 va_list vl ;
831794 va_start (vl , request_body );
832- format_body (request_body , vl );
795+ const bool formatted = format_body (request_body , vl );
833796 va_end (vl );
797+ if (!formatted ) {
798+ memzero (strbuf , sizeof (strbuf ));
799+ return false;
800+ }
834801
835802 /* Send button request */
836803 ButtonRequest resp ;
@@ -852,8 +819,12 @@ bool review_without_button_request(const char* request_title,
852819
853820 va_list vl ;
854821 va_start (vl , request_body );
855- format_body (request_body , vl );
822+ const bool formatted = format_body (request_body , vl );
856823 va_end (vl );
824+ if (!formatted ) {
825+ memzero (strbuf , sizeof (strbuf ));
826+ return false;
827+ }
857828
858829 const bool shown =
859830 confirm_helper (request_title , strbuf , & layout_standard_notification ,
@@ -869,8 +840,12 @@ bool review_with_icon(ButtonRequestType type, IconType iconNum,
869840
870841 va_list vl ;
871842 va_start (vl , request_body );
872- format_body (request_body , vl );
843+ const bool formatted = format_body (request_body , vl );
873844 va_end (vl );
845+ if (!formatted ) {
846+ memzero (strbuf , sizeof (strbuf ));
847+ return false;
848+ }
874849
875850 /* Send button request */
876851 ButtonRequest resp ;
@@ -892,8 +867,12 @@ bool review_immediate(ButtonRequestType type, const char* request_title,
892867
893868 va_list vl ;
894869 va_start (vl , request_body );
895- format_body (request_body , vl );
870+ const bool formatted = format_body (request_body , vl );
896871 va_end (vl );
872+ if (!formatted ) {
873+ memzero (strbuf , sizeof (strbuf ));
874+ return false;
875+ }
897876
898877 /* Send button request */
899878 ButtonRequest resp ;
0 commit comments