@@ -131,8 +131,9 @@ func diffLineFromC(line *C.git_diff_line) DiffLine {
131
131
}
132
132
133
133
type Diff struct {
134
- ptr * C.git_diff
135
- repo * Repository
134
+ ptr * C.git_diff
135
+ repo * Repository
136
+ runFinalizer bool
136
137
}
137
138
138
139
func (diff * Diff ) NumDeltas () (int , error ) {
@@ -165,8 +166,9 @@ func newDiffFromC(ptr *C.git_diff, repo *Repository) *Diff {
165
166
}
166
167
167
168
diff := & Diff {
168
- ptr : ptr ,
169
- repo : repo ,
169
+ ptr : ptr ,
170
+ repo : repo ,
171
+ runFinalizer : true ,
170
172
}
171
173
172
174
runtime .SetFinalizer (diff , (* Diff ).Free )
@@ -177,6 +179,11 @@ func (diff *Diff) Free() error {
177
179
if diff .ptr == nil {
178
180
return ErrInvalid
179
181
}
182
+ if ! diff .runFinalizer {
183
+ // This is the case with the Diff objects that are involved in the DiffNotifyCallback.
184
+ diff .ptr = nil
185
+ return nil
186
+ }
180
187
runtime .SetFinalizer (diff , nil )
181
188
C .git_diff_free (diff .ptr )
182
189
diff .ptr = nil
@@ -579,9 +586,9 @@ var (
579
586
)
580
587
581
588
type diffNotifyData struct {
582
- Callback DiffNotifyCallback
583
- Diff * Diff
584
- Error error
589
+ Callback DiffNotifyCallback
590
+ Repository * Repository
591
+ Error error
585
592
}
586
593
587
594
//export diffNotifyCb
@@ -595,11 +602,20 @@ func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, m
595
602
}
596
603
597
604
if data != nil {
598
- if data .Diff == nil {
599
- data .Diff = newDiffFromC (diff_so_far , nil )
605
+ // We are not taking ownership of this diff pointer, so no finalizer is set.
606
+ diff := & Diff {
607
+ ptr : diff_so_far ,
608
+ repo : data .Repository ,
609
+ runFinalizer : false ,
600
610
}
601
611
602
- err := data .Callback (data .Diff , diffDeltaFromC (delta_to_add ), C .GoString (matched_pathspec ))
612
+ err := data .Callback (diff , diffDeltaFromC (delta_to_add ), C .GoString (matched_pathspec ))
613
+
614
+ // Since the callback could theoretically keep a reference to the diff
615
+ // (which could be freed by libgit2 if an error occurs later during the
616
+ // diffing process), this converts a use-after-free (terrible!) into a nil
617
+ // dereference ("just" pretty bad).
618
+ diff .ptr = nil
603
619
604
620
if err == ErrDeltaSkip {
605
621
return 1
@@ -613,11 +629,12 @@ func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, m
613
629
return 0
614
630
}
615
631
616
- func diffOptionsToC (opts * DiffOptions ) (copts * C.git_diff_options , notifyData * diffNotifyData ) {
632
+ func diffOptionsToC (opts * DiffOptions , repo * Repository ) (copts * C.git_diff_options ) {
617
633
cpathspec := C.git_strarray {}
618
634
if opts != nil {
619
- notifyData = & diffNotifyData {
620
- Callback : opts .NotifyCallback ,
635
+ notifyData := & diffNotifyData {
636
+ Callback : opts .NotifyCallback ,
637
+ Repository : repo ,
621
638
}
622
639
623
640
if opts .Pathspec != nil {
@@ -670,7 +687,7 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (
670
687
newPtr = newTree .cast_ptr
671
688
}
672
689
673
- copts , notifyData := diffOptionsToC (opts )
690
+ copts := diffOptionsToC (opts , v )
674
691
defer freeDiffOptions (copts )
675
692
676
693
runtime .LockOSThread ()
@@ -682,10 +699,6 @@ func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (
682
699
if ecode < 0 {
683
700
return nil , MakeGitError (ecode )
684
701
}
685
-
686
- if notifyData != nil && notifyData .Diff != nil {
687
- return notifyData .Diff , nil
688
- }
689
702
return newDiffFromC (diffPtr , v ), nil
690
703
}
691
704
@@ -697,7 +710,7 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff,
697
710
oldPtr = oldTree .cast_ptr
698
711
}
699
712
700
- copts , notifyData := diffOptionsToC (opts )
713
+ copts := diffOptionsToC (opts , v )
701
714
defer freeDiffOptions (copts )
702
715
703
716
runtime .LockOSThread ()
@@ -708,10 +721,6 @@ func (v *Repository) DiffTreeToWorkdir(oldTree *Tree, opts *DiffOptions) (*Diff,
708
721
if ecode < 0 {
709
722
return nil , MakeGitError (ecode )
710
723
}
711
-
712
- if notifyData != nil && notifyData .Diff != nil {
713
- return notifyData .Diff , nil
714
- }
715
724
return newDiffFromC (diffPtr , v ), nil
716
725
}
717
726
@@ -728,7 +737,7 @@ func (v *Repository) DiffTreeToIndex(oldTree *Tree, index *Index, opts *DiffOpti
728
737
indexPtr = index .ptr
729
738
}
730
739
731
- copts , notifyData := diffOptionsToC (opts )
740
+ copts := diffOptionsToC (opts , v )
732
741
defer freeDiffOptions (copts )
733
742
734
743
runtime .LockOSThread ()
@@ -740,10 +749,6 @@ func (v *Repository) DiffTreeToIndex(oldTree *Tree, index *Index, opts *DiffOpti
740
749
if ecode < 0 {
741
750
return nil , MakeGitError (ecode )
742
751
}
743
-
744
- if notifyData != nil && notifyData .Diff != nil {
745
- return notifyData .Diff , nil
746
- }
747
752
return newDiffFromC (diffPtr , v ), nil
748
753
}
749
754
@@ -755,7 +760,7 @@ func (v *Repository) DiffTreeToWorkdirWithIndex(oldTree *Tree, opts *DiffOptions
755
760
oldPtr = oldTree .cast_ptr
756
761
}
757
762
758
- copts , notifyData := diffOptionsToC (opts )
763
+ copts := diffOptionsToC (opts , v )
759
764
defer freeDiffOptions (copts )
760
765
761
766
runtime .LockOSThread ()
@@ -766,10 +771,6 @@ func (v *Repository) DiffTreeToWorkdirWithIndex(oldTree *Tree, opts *DiffOptions
766
771
if ecode < 0 {
767
772
return nil , MakeGitError (ecode )
768
773
}
769
-
770
- if notifyData != nil && notifyData .Diff != nil {
771
- return notifyData .Diff , nil
772
- }
773
774
return newDiffFromC (diffPtr , v ), nil
774
775
}
775
776
@@ -781,7 +782,7 @@ func (v *Repository) DiffIndexToWorkdir(index *Index, opts *DiffOptions) (*Diff,
781
782
indexPtr = index .ptr
782
783
}
783
784
784
- copts , notifyData := diffOptionsToC (opts )
785
+ copts := diffOptionsToC (opts , v )
785
786
defer freeDiffOptions (copts )
786
787
787
788
runtime .LockOSThread ()
@@ -792,10 +793,6 @@ func (v *Repository) DiffIndexToWorkdir(index *Index, opts *DiffOptions) (*Diff,
792
793
if ecode < 0 {
793
794
return nil , MakeGitError (ecode )
794
795
}
795
-
796
- if notifyData != nil && notifyData .Diff != nil {
797
- return notifyData .Diff , nil
798
- }
799
796
return newDiffFromC (diffPtr , v ), nil
800
797
}
801
798
@@ -819,20 +816,23 @@ func DiffBlobs(oldBlob *Blob, oldAsPath string, newBlob *Blob, newAsPath string,
819
816
handle := pointerHandles .Track (data )
820
817
defer pointerHandles .Untrack (handle )
821
818
819
+ var repo * Repository
822
820
var oldBlobPtr , newBlobPtr * C.git_blob
823
821
if oldBlob != nil {
824
822
oldBlobPtr = oldBlob .cast_ptr
823
+ repo = oldBlob .repo
825
824
}
826
825
if newBlob != nil {
827
826
newBlobPtr = newBlob .cast_ptr
827
+ repo = newBlob .repo
828
828
}
829
829
830
830
oldBlobPath := C .CString (oldAsPath )
831
831
defer C .free (unsafe .Pointer (oldBlobPath ))
832
832
newBlobPath := C .CString (newAsPath )
833
833
defer C .free (unsafe .Pointer (newBlobPath ))
834
834
835
- copts , _ := diffOptionsToC (opts )
835
+ copts := diffOptionsToC (opts , repo )
836
836
defer freeDiffOptions (copts )
837
837
838
838
runtime .LockOSThread ()
0 commit comments